From 5469ba1d49df7c8371022b11eb7f08f217638943 Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Wed, 23 Nov 2016 19:02:02 +0100 Subject: [PATCH 1/9] 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 949924ec7..a49ef3035 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 000000000..5b6b66775 --- /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 29cad6e38..003fad819 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 000000000..48422e909 --- /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 000000000..eab6b7a05 --- /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 000000000..0287863d6 --- /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 000000000..cf032e81c --- /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 000000000..2471c33b3 --- /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 000000000..477aec7ed --- /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 000000000..663d2a59f --- /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 000000000..f01dc4492 --- /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 000000000..503e97fb9 --- /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 000000000..6b17fecc4 --- /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 000000000..9744254dd --- /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 000000000..1316bf966 --- /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 000000000..d5679a1df --- /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 */ + +/** @} */ From 144a0700f54ede83364094eca21fbc467e5a1041 Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Wed, 23 Nov 2016 19:05:35 +0100 Subject: [PATCH 2/9] can: add autoinit basic support --- Makefile.dep | 1 + sys/auto_init/Makefile | 4 ++++ sys/auto_init/auto_init.c | 8 ++++++++ sys/auto_init/can/Makefile | 3 +++ sys/auto_init/can/auto_init_can.c | 32 +++++++++++++++++++++++++++++++ 5 files changed, 48 insertions(+) create mode 100644 sys/auto_init/can/Makefile create mode 100644 sys/auto_init/can/auto_init_can.c diff --git a/Makefile.dep b/Makefile.dep index a49ef3035..19bac9393 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -568,6 +568,7 @@ endif ifneq (,$(filter can,$(USEMODULE))) USEMODULE += can_raw + USEMODULE += auto_init_can ifneq (,$(filter can_mbox,$(USEMODULE))) USEMODULE += core_mbox endif diff --git a/sys/auto_init/Makefile b/sys/auto_init/Makefile index 3328c8443..9ea42ebab 100644 --- a/sys/auto_init/Makefile +++ b/sys/auto_init/Makefile @@ -12,4 +12,8 @@ ifneq (,$(filter auto_init_storage,$(USEMODULE))) DIRS += storage endif +ifneq (,$(filter auto_init_can,$(USEMODULE))) +DIRS += can +endif + include $(RIOTBASE)/Makefile.base diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index c6644a507..7b6bcb7af 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -361,4 +361,12 @@ void auto_init(void) #endif #endif /* MODULE_AUTO_INIT_STORAGE */ + +#ifdef MODULE_AUTO_INIT_CAN + DEBUG("auto_init CAN\n"); + + extern void auto_init_candev(void); + auto_init_candev(); + +#endif /* MODULE_AUTO_INIT_CAN */ } diff --git a/sys/auto_init/can/Makefile b/sys/auto_init/can/Makefile new file mode 100644 index 000000000..af49afada --- /dev/null +++ b/sys/auto_init/can/Makefile @@ -0,0 +1,3 @@ +MODULE = auto_init_can + +include $(RIOTBASE)/Makefile.base diff --git a/sys/auto_init/can/auto_init_can.c b/sys/auto_init/can/auto_init_can.c new file mode 100644 index 000000000..93eb48774 --- /dev/null +++ b/sys/auto_init/can/auto_init_can.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup auto_init + * @{ + * @file + * @brief initializes can device init function + * + * @author Toon Stegen + * @author Vincent Dupont + * @author Aurelien Gonce + * @} + */ + +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include "can/dll.h" + +void auto_init_candev(void) +{ + DEBUG("auto_init_can: init dll\n"); + can_dll_init(); +} From 68f1ea8fd0622965c68ebcc03e3f0bf81e21a2de Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Wed, 23 Nov 2016 19:06:59 +0100 Subject: [PATCH 3/9] native: add a CAN device using socketCAN --- Makefile.dep | 4 + boards/native/Makefile.dep | 7 + cpu/native/Makefile | 4 + cpu/native/can/Makefile | 3 + cpu/native/can/candev_linux.c | 558 +++++++++++++++++++++++ cpu/native/include/candev_linux.h | 104 +++++ cpu/native/include/candev_linux_params.h | 43 ++ cpu/native/startup.c | 36 +- sys/auto_init/can/auto_init_can.c | 4 + sys/auto_init/can/auto_init_can_native.c | 54 +++ 10 files changed, 816 insertions(+), 1 deletion(-) create mode 100644 cpu/native/can/Makefile create mode 100644 cpu/native/can/candev_linux.c create mode 100644 cpu/native/include/candev_linux.h create mode 100644 cpu/native/include/candev_linux_params.h create mode 100644 sys/auto_init/can/auto_init_can_native.c diff --git a/Makefile.dep b/Makefile.dep index 19bac9393..267a121c8 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -566,6 +566,10 @@ ifneq (,$(filter evtimer,$(USEMODULE))) USEMODULE += xtimer endif +ifneq (,$(filter can_linux,$(USEMODULE))) + export LINKFLAGS += -lsocketcan +endif + ifneq (,$(filter can,$(USEMODULE))) USEMODULE += can_raw USEMODULE += auto_init_can diff --git a/boards/native/Makefile.dep b/boards/native/Makefile.dep index 543ab6b54..e65c41db0 100644 --- a/boards/native/Makefile.dep +++ b/boards/native/Makefile.dep @@ -5,3 +5,10 @@ endif ifneq (,$(filter mtd,$(USEMODULE))) USEMODULE += mtd_native endif + +ifneq (,$(filter can,$(USEMODULE))) + ifeq ($(shell uname -s),Linux) + USEMODULE += can_linux + CFLAGS += -DCAN_DLL_NUMOF=2 + endif +endif diff --git a/cpu/native/Makefile b/cpu/native/Makefile index f155c230d..9495a44a8 100644 --- a/cpu/native/Makefile +++ b/cpu/native/Makefile @@ -10,6 +10,10 @@ ifneq (,$(filter mtd_native,$(USEMODULE))) DIRS += mtd endif +ifneq (,$(filter can_linux,$(USEMODULE))) + DIRS += can +endif + include $(RIOTBASE)/Makefile.base INCLUDES = $(NATIVEINCLUDES) diff --git a/cpu/native/can/Makefile b/cpu/native/can/Makefile new file mode 100644 index 000000000..db78e101a --- /dev/null +++ b/cpu/native/can/Makefile @@ -0,0 +1,3 @@ +MODULE = can_linux + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/native/can/candev_linux.c b/cpu/native/can/candev_linux.c new file mode 100644 index 000000000..a481c63a0 --- /dev/null +++ b/cpu/native/can/candev_linux.c @@ -0,0 +1,558 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup native_cpu + * @{ + * + * @file + * @brief Implementation of simulated CAN controller driver using SocketCAN on Linux + * + * @author Hermann Lelong + * @author Aurelien Gonce + * @author Vincent Dupont + * @} + */ + +#if !defined(__linux__) +#error "MODULE can_linux is only available on Linux" +#else + +#include +#include +#include +#include + +#include +#include + +#include "native_internal.h" +#include "can/device.h" +#include "candev_linux.h" +#include "thread.h" +#include "mutex.h" +#include "async_read.h" +#include "sched.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static int _init(candev_t *candev); +static int _send(candev_t *candev, const struct can_frame *frame); +static void _isr(candev_t *candev); +static int _set(candev_t *candev, canopt_t opt, void *value, size_t value_len); +static int _get(candev_t *candev, canopt_t opt, void *value, size_t max_len); +static int _abort(candev_t *candev, const struct can_frame *frame); +static int _set_filter(candev_t *candev, const struct can_filter *filter); +static int _remove_filter(candev_t *candev, const struct can_filter *filter); +static int _power_up(candev_t *candev); +static int _power_down(candev_t *candev); + +static int _set_bittiming(candev_linux_t *dev, struct can_bittiming *bittiming); + +static const candev_driver_t candev_linux_driver = { + .send = _send, + .init = _init, + .isr = _isr, + .get = _get, + .set = _set, + .abort = _abort, + .set_filter = _set_filter, + .remove_filter = _remove_filter, +}; + +static candev_event_t _can_error_to_can_evt(struct can_frame can_frame_err); +static void _callback_can_sigio(int sock, void *arg); + +candev_linux_conf_t candev_linux_conf[CAN_DLL_NUMOF] = { +#if CAN_DLL_NUMOF >= 1 + { + .interface_name = "vcan0", + }, +#endif +#if CAN_DLL_NUMOF >= 2 + { + .interface_name = "vcan1", + } +#endif +}; + +int candev_linux_init(candev_linux_t *dev, const candev_linux_conf_t *conf) +{ + memset(dev, 0, sizeof(candev_linux_t)); + dev->candev.driver = &candev_linux_driver; + dev->conf = conf; + dev->candev.bittiming.bitrate = CANDEV_LINUX_DEFAULT_BITRATE; + dev->candev.bittiming.sample_point = CANDEV_LINUX_DEFAULT_SPT; + + return 0; +} + +static candev_event_t _can_error_to_can_evt(struct can_frame can_frame_err) +{ + candev_event_t can_evt = CANDEV_EVENT_NOEVENT; + can_err_mask_t can_err_type = can_frame_err.can_id & CAN_ERR_MASK; + + if (can_err_type & CAN_ERR_TX_TIMEOUT) { + can_evt = CANDEV_EVENT_TX_ERROR; + } + else if (can_err_type & CAN_ERR_CRTL) { + switch(can_frame_err.data[1]) { + case CAN_ERR_CRTL_RX_OVERFLOW: + can_evt = CANDEV_EVENT_RX_ERROR; + break; + case CAN_ERR_CRTL_TX_OVERFLOW: + can_evt = CANDEV_EVENT_TX_ERROR; + break; + case CAN_ERR_CRTL_RX_PASSIVE: + case CAN_ERR_CRTL_TX_PASSIVE: + can_evt = CANDEV_EVENT_ERROR_PASSIVE; + break; + case CAN_ERR_CRTL_RX_WARNING: + case CAN_ERR_CRTL_TX_WARNING: + can_evt = CANDEV_EVENT_ERROR_WARNING; + break; + } + } + else if (can_err_type & CAN_ERR_BUSOFF) { + can_evt = CANDEV_EVENT_BUS_OFF; + } + + return can_evt; +} + +static void _callback_can_sigio(int sockfd, void *arg) +{ + (void) sockfd; + candev_linux_t *dev = (candev_linux_t *) arg; + + if (dev->candev.event_callback) { + dev->candev.event_callback(&dev->candev, CANDEV_EVENT_ISR, NULL); + } + + native_async_read_continue(sockfd); + + if (sched_context_switch_request) { + thread_yield_higher(); + } +} + +static int _init(candev_t *candev) +{ + struct sockaddr_can addr; + struct ifreq ifr; + int ret; + + DEBUG("Will start linux CAN init\n"); + candev_linux_t *dev = (candev_linux_t *)candev; + + if ((strlen(dev->conf->interface_name) == 0) + || (strlen(dev->conf->interface_name) > CAN_MAX_SIZE_INTERFACE_NAME)) { + real_printf("Error: Invalid can iface, too short or too long \n"); + return -1; + } + + dev->sock = real_socket(PF_CAN, SOCK_RAW, CAN_RAW); + + if (dev->sock < 0) { + real_printf("CAN config KO, socket nr = %i \n", dev->sock); + return -1; + } + + can_err_mask_t err_mask = CAN_ERR_TX_TIMEOUT | + CAN_ERR_BUSOFF | + CAN_ERR_CRTL; + ret = real_setsockopt(dev->sock, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, + &err_mask, sizeof(err_mask)); + + if (ret < 0) { + real_printf("Error: setsockopt failed\n"); + real_close(dev->sock); + return -1; + } + + strcpy(ifr.ifr_name, dev->conf->interface_name); + ret = real_ioctl(dev->sock, SIOCGIFINDEX, &ifr); + + if (ret < 0) { + real_printf("Error: Invalid can iface %s\n", dev->conf->interface_name); + real_close(dev->sock); + return -1; + } + + native_async_read_setup(); + /* This func will also automatically configure socket to be asynchronous */ + /* and to activate SIGIO */ + native_async_read_add_handler(dev->sock, (void *) dev, _callback_can_sigio); + + addr.can_family = AF_CAN; + addr.can_ifindex = ifr.ifr_ifindex; + + real_bind(dev->sock, (struct sockaddr *)&addr, sizeof(addr)); + + _set_bittiming(dev, &candev->bittiming); + + DEBUG("CAN linux device ready\n"); + + return 0; +} + +static int _send(candev_t *candev, const struct can_frame *frame) +{ + int nbytes; + candev_linux_t *dev = (candev_linux_t *)candev; + + nbytes = real_write(dev->sock, frame, sizeof(struct can_frame)); + + if (nbytes < frame->can_dlc) { + real_printf("CAN write op failed, nbytes=%i\n", nbytes); + return -1; + } + + if (dev->candev.event_callback) { + dev->candev.event_callback(&dev->candev, CANDEV_EVENT_TX_CONFIRMATION, (void *)frame); + } + + return 0; +} + +static void _isr(candev_t *candev) +{ + int nbytes; + struct can_frame rcv_frame; + candev_linux_t *dev = (candev_linux_t *)candev; + + if (dev == NULL) { + return; + } + + DEBUG("candev_native _isr: CAN SIGIO interrupt received, sock = %i\n", dev->sock); + nbytes = real_read(dev->sock, &rcv_frame, sizeof(struct can_frame)); + + if (nbytes < 0) { /* SIGIO signal was probably due to an error with the socket */ + DEBUG("candev_native _isr: read: error during read\n"); + return; + } + + if (nbytes < (int)sizeof(struct can_frame)) { + DEBUG("candev_native _isr: read: incomplete CAN frame\n"); + return; + } + + if (rcv_frame.can_id & CAN_ERR_FLAG) { + DEBUG("candev_native _isr: error frame\n"); + candev_event_t evt = _can_error_to_can_evt(rcv_frame); + if ((evt != CANDEV_EVENT_NOEVENT) && (dev->candev.event_callback)) { + dev->candev.event_callback(&dev->candev, evt, NULL); + } + return; + } + + if (rcv_frame.can_id & CAN_RTR_FLAG) { + DEBUG("candev_native _isr: rtr frame\n"); + return; + } + + if (dev->candev.event_callback) { + DEBUG("candev_native _isr: calling event callback\n"); + dev->candev.event_callback(&dev->candev, CANDEV_EVENT_RX_INDICATION, &rcv_frame); + } + +} + +static int _set_bittiming(candev_linux_t *dev, struct can_bittiming *bittiming) +{ + int res; + + dev->candev.bittiming = *bittiming; + + DEBUG("bitrate = %d, brp= %d, phase_seg1 = %d, phase_seg2 = %d, sjw = %d\n", + dev->candev.bittiming.bitrate, dev->candev.bittiming.brp, + dev->candev.bittiming.phase_seg1, dev->candev.bittiming.phase_seg2, + dev->candev.bittiming.sjw); + + /* bitrate setting */ + DEBUG("_set: setting %s down\n", dev->conf->interface_name); + res = can_do_stop(dev->conf->interface_name); + if (res < 0) { + return res; + } + DEBUG("_set: setting bittiming to %s\n", dev->conf->interface_name); + res = can_set_bitrate(dev->conf->interface_name, dev->candev.bittiming.bitrate); + can_get_bittiming(dev->conf->interface_name, &dev->candev.bittiming); + DEBUG("_set: setting %s up\n", dev->conf->interface_name); + can_do_start(dev->conf->interface_name); + + return res; +} + +static int _set(candev_t *candev, canopt_t opt, void *value, size_t value_len) +{ + candev_linux_t *dev = (candev_linux_t *) candev; + int res = 0; + + switch (opt) { + case CANOPT_BITTIMING: + DEBUG("candev_linux: CANOPT_BITTIMING\n"); + + if (strncmp(dev->conf->interface_name, "can", strlen("can"))) { + DEBUG("candev_native: _set: error interface is not real can\n"); + return -EINVAL; + } + + if (value == NULL) { + DEBUG("candev_native: _set: error value NULL\n"); + return -EOVERFLOW; + } + + if (value_len < sizeof(struct can_bittiming)) { + DEBUG("candev_native: _set: error size of bittiming\n"); + return -EOVERFLOW; + } + + res = _set_bittiming(dev, value); + + break; + case CANOPT_STATE: + switch (*((canopt_state_t *)value)) { + case CANOPT_STATE_SLEEP: + case CANOPT_STATE_OFF: + _power_down(candev); + break; + default: + _power_up(candev); + break; + } + break; + default: + DEBUG("CAN set, not supported opt\n"); + res = -ENOTSUP; + break; + } + + return res; +} + +static int _get(candev_t *candev, canopt_t opt, void *value, size_t max_len) +{ + candev_linux_t *dev = (candev_linux_t *) candev; + int res = 0; + + switch (opt) { + case CANOPT_BITTIMING: + if (max_len < sizeof(struct can_bittiming)) { + res = -EOVERFLOW; + break; + } + if (value == NULL) { + res = -EINVAL; + break; + } + if (can_get_bittiming(dev->conf->interface_name, value) == 0) { + res = sizeof(struct can_bittiming); + } + else { + res = -ENOTSUP; + } + break; + case CANOPT_BITTIMING_CONST: + if (max_len < sizeof(struct can_bittiming_const)) { + res = -EOVERFLOW; + break; + } + if (value == NULL) { + res = -EINVAL; + break; + } + if (can_get_bittiming_const(dev->conf->interface_name, value) == 0) { + res = sizeof(struct can_bittiming_const); + } + else { + res = -ENOTSUP; + } + break; + case CANOPT_CLOCK: + if (max_len < sizeof(uint32_t)) { + res = -EOVERFLOW; + break; + } + if (value == NULL) { + res = -EINVAL; + break; + } + { + struct can_clock clock; + if (can_get_clock(dev->conf->interface_name, &clock) == 0) { + *((uint32_t *)value) = clock.freq; + res = sizeof(uint32_t); + } + else { + res = -ENOTSUP; + } + } + break; + case CANOPT_TEC: + case CANOPT_REC: + if (max_len < sizeof(uint16_t)) { + res = -EOVERFLOW; + break; + } + if (value == NULL) { + res = -EINVAL; + break; + } + { + struct can_berr_counter bc; + if (can_get_berr_counter(dev->conf->interface_name, &bc) == 0) { + if (opt == CANOPT_TEC) { + *((uint16_t *)value) = bc.txerr; + } + else { + *((uint16_t *)value) = bc.rxerr; + } + res = sizeof(uint16_t); + } + else { + res = -ENOTSUP; + } + } + break; + case CANOPT_RX_FILTERS: { + if (max_len % sizeof(struct can_filter) != 0) { + res = -EOVERFLOW; + break; + } + struct can_filter *list = value; + size_t i; + for (i = 0; (i < CANDEV_LINUX_MAX_FILTERS_RX) + && (dev->filters[i].can_id != 0) + && (i < (max_len / sizeof(struct can_filter))); i++) { + list[i] = dev->filters[i]; + } + res = i * sizeof(struct can_filter); + break; } + default: + DEBUG("CAN get, not supported op\n"); + res = -ENOTSUP; + break; + } + + return res; +} + +static int _set_filter(candev_t *candev, const struct can_filter *filter) +{ + candev_linux_t *dev = (candev_linux_t *)candev; + + if (filter == NULL) { + DEBUG("candev_native: _set_filter: error filter NULL\n"); + return -EOVERFLOW; + } + + DEBUG("candev_native: _set_filter: candev=%p, filter: f=%x m=%x on sock: %i\n", + (void *)candev, filter->can_id, filter->can_mask, dev->sock + ); + uint32_t i; + for (i = 0; i < CANDEV_LINUX_MAX_FILTERS_RX; i++) { + if (dev->filters[i].can_id == filter->can_id) { + DEBUG("candev_native: _set_filter: filter already set\n"); + return 0; + } + else if (dev->filters[i].can_id == 0) { + break; + } + } + if (i == CANDEV_LINUX_MAX_FILTERS_RX) { + DEBUG("candev_native: _set_filter: no more filters available\n"); + return -EOVERFLOW; + } + for (i = 0; i < CANDEV_LINUX_MAX_FILTERS_RX; i++) { + if (dev->filters[i].can_id == 0) { + /* Only 29 bits must be used for masks in SocketCAN */ + dev->filters[i] = *filter; + dev->filters[i].can_mask &= CAN_EFF_MASK; + DEBUG("candev_native: _set_filter: filter:ID=0x%x\n", filter->can_id); + DEBUG("candev_native: _set_filter: mask=0x%x\n", filter->can_mask); + break; + } + } + i++; + DEBUG("%" PRIu32 " filters will be set\n", i); + real_setsockopt(dev->sock, SOL_CAN_RAW, CAN_RAW_FILTER, dev->filters, + sizeof(struct can_filter) * i); + + return i; +} + +static int _remove_filter(candev_t *candev, const struct can_filter *filter) +{ + candev_linux_t *dev = (candev_linux_t *)candev; + + if (filter == NULL) { + DEBUG("candev_native: _remove_filter: error filter NULL\n"); + return -EOVERFLOW; + } + + uint32_t i; + for (i = 0; i < CANDEV_LINUX_MAX_FILTERS_RX; i++) { + if ((dev->filters[i].can_id == filter->can_id ) + && (dev->filters[i].can_mask == (filter->can_mask & CAN_EFF_MASK))) { + if (i < CANDEV_LINUX_MAX_FILTERS_RX - 1) { + memmove(&dev->filters[i], &dev->filters[i + 1], + sizeof(dev->filters[i]) * (CANDEV_LINUX_MAX_FILTERS_RX - i - 1)); + } + dev->filters[CANDEV_LINUX_MAX_FILTERS_RX - 1].can_id = 0; + break; + + } + else if (dev->filters[i].can_id == 0) { + DEBUG("candev_native: _remove_filter: error filter not found\n"); + return -EOVERFLOW; + } + } + + if (i == CANDEV_LINUX_MAX_FILTERS_RX) { + DEBUG("candev_native: _remove_filter: error filter not found\n"); + return -EOVERFLOW; + } + + for (i = 0; i < CANDEV_LINUX_MAX_FILTERS_RX; i++) { + if (dev->filters[i].can_id == 0) { + break; + } + } + + DEBUG("%" PRIu32 " filters will be set\n", i); + real_setsockopt(dev->sock, SOL_CAN_RAW, CAN_RAW_FILTER, dev->filters, + sizeof(struct can_filter) * i); + + return 0; +} + +static int _abort(candev_t *candev, const struct can_frame *frame) +{ + (void)frame; + (void)candev; + + return 0; +} + +static int _power_down(candev_t *candev) +{ + (void)candev; + + return 0; +} + +static int _power_up(candev_t *candev) +{ + (void)candev; + + return 0; +} +#endif /* defined(__linux__) */ diff --git a/cpu/native/include/candev_linux.h b/cpu/native/include/candev_linux.h new file mode 100644 index 000000000..54f451b7e --- /dev/null +++ b/cpu/native/include/candev_linux.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup native_cpu + * @ingroup drivers_can + * @defgroup candev_linux SocketCAN driver + * @{ + * + * @file + * @brief Implementation of simulated CAN controller driver using SocketCAN on Linux + * + * @author Hermann Lelong + * @author Aurelien Gonce + * @author Vincent Dupont + * @} + */ + +#ifndef CANDEV_LINUX_H +#define CANDEV_LINUX_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__linux__) /* SocketCAN is supported only on Linux */ || defined(DOXYGEN) + +#include + +#include "can/candev.h" +#include "mutex.h" + +/** + * Maximum size of an interface name + */ +#define CAN_MAX_SIZE_INTERFACE_NAME (5) + +/** + * Linux candev configuration + */ +typedef struct candev_linux_conf { + /** local interface name */ + char interface_name[CAN_MAX_SIZE_INTERFACE_NAME + 1]; +} candev_linux_conf_t; + +#ifndef CANDEV_LINUX_MAX_FILTERS_RX +/** + * Max number of rx filters which can be set + */ +#define CANDEV_LINUX_MAX_FILTERS_RX (16) +#endif + +#ifndef CANDEV_LINUX_DEFAULT_BITRATE +/** + * Default bitrate setup + */ +#define CANDEV_LINUX_DEFAULT_BITRATE (500000) +#endif + +#ifndef CANDEV_LINUX_DEFAULT_SPT +/** + * Default sampling point setup + */ +#define CANDEV_LINUX_DEFAULT_SPT (875) +#endif + +/** + * @brief The candev_linux struct + */ +typedef struct candev_linux { + candev_t candev; /**< candev base structure */ + int sock; /**< local socket id */ + const candev_linux_conf_t *conf; /**< device configuration */ + /** filter list */ + struct can_filter filters[CANDEV_LINUX_MAX_FILTERS_RX]; +} candev_linux_t; + +/** + * @brief Device specific initialization function + * + * @param[inout] dev the device to initialize + * @param[in] conf the device configuration + * + * @return 0 on success + */ +int candev_linux_init(candev_linux_t *dev, const candev_linux_conf_t *conf); + +/** + * @brief Array containing socketCAN device names + */ +extern candev_linux_conf_t candev_linux_conf[CAN_DLL_NUMOF]; + +#endif /* defined(__linux__) */ + +#ifdef __cplusplus +} +#endif + +#endif /* CANDEV_LINUX_H */ diff --git a/cpu/native/include/candev_linux_params.h b/cpu/native/include/candev_linux_params.h new file mode 100644 index 000000000..68ce965c1 --- /dev/null +++ b/cpu/native/include/candev_linux_params.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup candev_linux + * @{ + * + * @file + * @brief Default linux can config + * + * @author Vincent Dupont + * @} + */ + +#ifndef CANDEV_LINUX_PARAMS_H +#define CANDEV_LINUX_PARAMS_H + +#include "candev_linux.h" +#include "can/device.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Default parameters (device names) + */ +static candev_params_t candev_linux_params[] = { + { .name = "can0", }, + { .name = "can1", }, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* CANDEV_LINUX_PARAMS_H */ +/** @} */ diff --git a/cpu/native/startup.c b/cpu/native/startup.c index 2cbf4cd67..1d08b0963 100644 --- a/cpu/native/startup.c +++ b/cpu/native/startup.c @@ -67,17 +67,23 @@ const char *_native_unix_socket_path = NULL; netdev_tap_params_t netdev_tap_params[NETDEV_TAP_MAX]; #endif - #ifdef MODULE_MTD_NATIVE #include "board.h" #include "mtd_native.h" #endif +#ifdef MODULE_CAN_LINUX +#include "candev_linux.h" +#endif static const char short_opts[] = ":hi:s:deEoc:" #ifdef MODULE_MTD_NATIVE "m:" +#endif +#ifdef MODULE_CAN_LINUX + "n:" #endif ""; + static const struct option long_opts[] = { { "help", no_argument, NULL, 'h' }, { "id", required_argument, NULL, 'i' }, @@ -89,6 +95,9 @@ static const struct option long_opts[] = { { "uart-tty", required_argument, NULL, 'c' }, #ifdef MODULE_MTD_NATIVE { "mtd", required_argument, NULL, 'm' }, +#endif +#ifdef MODULE_CAN_LINUX + { "can", required_argument, NULL, 'n' }, #endif { NULL, 0, NULL, '\0' }, }; @@ -249,6 +258,12 @@ void usage_exit(int status) real_printf( " -m , --mtd=\n" " specify the file name of mtd emulated device\n"); +#endif +#if defined(MODULE_CAN_LINUX) + real_printf( +" -n :, --can :\n" +" specify CAN interface to use for CAN device #\n" +" max number of CAN device: %d\n", CAN_DLL_NUMOF); #endif real_exit(status); } @@ -325,6 +340,25 @@ __attribute__((constructor)) static void startup(int argc, char **argv, char **e case 'm': ((mtd_native_dev_t *)mtd0)->fname = strndup(optarg, PATH_MAX - 1); break; +#endif +#if defined(MODULE_CAN_LINUX) + case 'n':{ + int i; + i = atol(optarg); + if (i >= (int)CAN_DLL_NUMOF) { + usage_exit(EXIT_FAILURE); + } + while ((*optarg != ':') && (*optarg != '\0')) { + optarg++; + } + if (*optarg == '\0') { + usage_exit(EXIT_FAILURE); + } + optarg++; + strncpy(candev_linux_conf[i].interface_name, optarg, + CAN_MAX_SIZE_INTERFACE_NAME); + } + break; #endif default: usage_exit(EXIT_FAILURE); diff --git a/sys/auto_init/can/auto_init_can.c b/sys/auto_init/can/auto_init_can.c index 93eb48774..e94cddbb8 100644 --- a/sys/auto_init/can/auto_init_can.c +++ b/sys/auto_init/can/auto_init_can.c @@ -29,4 +29,8 @@ void auto_init_candev(void) { DEBUG("auto_init_can: init dll\n"); can_dll_init(); +#ifdef MODULE_CAN_LINUX + extern void auto_init_can_native(void); + auto_init_can_native(); +#endif } diff --git a/sys/auto_init/can/auto_init_can_native.c b/sys/auto_init/can/auto_init_can_native.c new file mode 100644 index 000000000..7b0c44dc4 --- /dev/null +++ b/sys/auto_init/can/auto_init_can_native.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup auto_init + * @{ + * @file + * @brief initializes native can device + * + * @author Vincent Dupont + * @} + */ + +#ifdef MODULE_CAN_LINUX +#include "can/device.h" +#include "candev_linux_params.h" + +#define CANDEV_LINUX_NUMOF ((sizeof(candev_linux_params) / sizeof(candev_params_t))) + +#ifndef CANDEV_LINUX_STACKSIZE +#define CANDEV_LINUX_STACKSIZE (THREAD_STACKSIZE_DEFAULT + THREAD_EXTRA_STACKSIZE_PRINTF) +#endif + +#ifndef CANDEV_LINUX_BASE_PRIORITY +#define CANDEV_LINUX_BASE_PRIORITY (THREAD_PRIORITY_MAIN - CANDEV_LINUX_NUMOF - 2) +#endif + +static candev_dev_t candev_dev_linux[CANDEV_LINUX_NUMOF]; +static char _can_linux_stacks[CANDEV_LINUX_NUMOF][CANDEV_LINUX_STACKSIZE]; +static candev_linux_t candev_linux[CANDEV_LINUX_NUMOF]; + +void auto_init_can_native(void) { + + for (size_t i = 0; i < CANDEV_LINUX_NUMOF; i++) { + candev_linux_init(&candev_linux[i], &candev_linux_conf[i]); + candev_dev_linux[i].dev = (candev_t *)&candev_linux[i]; + candev_dev_linux[i].name = candev_linux_params[i].name; +#ifdef MODULE_CAN_PM + candev_dev_linux[i].rx_inactivity_timeout = candev_linux_params[i].rx_inactivity_timeout; + candev_dev_linux[i].tx_wakeup_timeout = candev_linux_params[i].tx_wakeup_timeout; +#endif + + can_device_init(_can_linux_stacks[i], CANDEV_LINUX_STACKSIZE, CANDEV_LINUX_BASE_PRIORITY + i, + candev_linux_params[i].name, &candev_dev_linux[i]); + } +} +#else +typedef int dont_be_pedantic; +#endif From 63ca443b05ee3bd3dc3eef4e48e487a7e14cea45 Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Wed, 23 Nov 2016 19:08:30 +0100 Subject: [PATCH 4/9] can: add ISO-TP support ISO-TP is ISO15765 transport protocol over CAN. Upper interface is located in sys/include/can/isotp.h. --- Makefile.dep | 4 + sys/auto_init/can/auto_init_can.c | 20 + sys/can/Makefile | 5 + sys/can/isotp/Makefile | 3 + sys/can/isotp/isotp.c | 898 ++++++++++++++++++++++++++++++ sys/include/can/isotp.h | 196 +++++++ 6 files changed, 1126 insertions(+) create mode 100644 sys/can/isotp/Makefile create mode 100644 sys/can/isotp/isotp.c create mode 100644 sys/include/can/isotp.h diff --git a/Makefile.dep b/Makefile.dep index 267a121c8..d4706ae62 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -579,6 +579,10 @@ ifneq (,$(filter can,$(USEMODULE))) USEMODULE += gnrc_pktbuf_static endif +ifneq (,$(filter can_isotp,$(USEMODULE))) + USEMODULE += xtimer +endif + ifneq (,$(filter random,$(USEMODULE))) # select default prng ifeq (,$(filter prng_%,$(USEMODULE))) diff --git a/sys/auto_init/can/auto_init_can.c b/sys/auto_init/can/auto_init_can.c index e94cddbb8..c5f9dfa1f 100644 --- a/sys/auto_init/can/auto_init_can.c +++ b/sys/auto_init/can/auto_init_can.c @@ -25,10 +25,30 @@ #include "can/dll.h" +#ifdef MODULE_CAN_ISOTP +#include "can/isotp.h" + +#ifndef ISOTP_STACK_SIZE +#define ISOTP_STACK_SIZE (THREAD_STACKSIZE_DEFAULT + THREAD_EXTRA_STACKSIZE_PRINTF) +#endif + +#ifndef ISOTP_PRIORITY +#define ISOTP_PRIORITY (THREAD_PRIORITY_MAIN - 2) +#endif + +static char isotp_stack[ISOTP_STACK_SIZE]; +#endif + void auto_init_candev(void) { DEBUG("auto_init_can: init dll\n"); can_dll_init(); + +#ifdef MODULE_CAN_ISOTP + DEBUG("auto_init_can: init isotp\n"); + isotp_init(isotp_stack, ISOTP_STACK_SIZE, ISOTP_PRIORITY, "isotp"); +#endif + #ifdef MODULE_CAN_LINUX extern void auto_init_can_native(void); auto_init_can_native(); diff --git a/sys/can/Makefile b/sys/can/Makefile index 48422e909..449d4878c 100644 --- a/sys/can/Makefile +++ b/sys/can/Makefile @@ -1 +1,6 @@ + +ifneq (,$(filter can_isotp,$(USEMODULE))) + DIRS += isotp +endif + include $(RIOTBASE)/Makefile.base diff --git a/sys/can/isotp/Makefile b/sys/can/isotp/Makefile new file mode 100644 index 000000000..9f32f4c6e --- /dev/null +++ b/sys/can/isotp/Makefile @@ -0,0 +1,3 @@ +MODULE = can_isotp + +include $(RIOTBASE)/Makefile.base diff --git a/sys/can/isotp/isotp.c b/sys/can/isotp/isotp.c new file mode 100644 index 000000000..8ee8c72b2 --- /dev/null +++ b/sys/can/isotp/isotp.c @@ -0,0 +1,898 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @file + * @brief ISO TP high level interface + * + * @author Vincent Dupont + */ + +#include +#include + +#include "net/gnrc/pktbuf.h" + +#include "can/isotp.h" +#include "can/common.h" +#include "can/raw.h" +#include "can/router.h" +#include "thread.h" +#include "mutex.h" +#include "timex.h" +#include "utlist.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#ifndef CAN_ISOTP_BS +#define CAN_ISOTP_BS 10 +#endif + +#ifndef CAN_ISOTP_STMIN +#define CAN_ISOTP_STMIN 5 +#endif + +#ifndef CAN_ISOTP_WFTMAX +#define CAN_ISOTP_WFTMAX 0 +#endif + +#ifndef CAN_ISOTP_MSG_QUEUE_SIZE +#define CAN_ISOTP_MSG_QUEUE_SIZE 64 +#endif + +#ifndef CAN_ISOTP_TIMEOUT_N_As +#define CAN_ISOTP_TIMEOUT_N_As (1 * US_PER_SEC) +#endif + +#ifndef CAN_ISOTP_TIMEOUT_N_Bs +#define CAN_ISOTP_TIMEOUT_N_Bs (1 * US_PER_SEC) +#endif + +#ifndef CAN_ISOTP_TIMEOUT_N_Ar +#define CAN_ISOTP_TIMEOUT_N_Ar (1 * US_PER_SEC) +#endif + +#ifndef CAN_ISOTP_TIMEOUT_N_Cr +#define CAN_ISOTP_TIMEOUT_N_Cr (1 * US_PER_SEC) +#endif + +enum { + ISOTP_IDLE = 0, + ISOTP_WAIT_FC, + ISOTP_WAIT_CF, + ISOTP_SENDING_SF, + ISOTP_SENDING_FF, + ISOTP_SENDING_CF, + ISOTP_SENDING_FC, + ISOTP_SENDING_NEXT_CF, +}; + +#define MAX_MSG_LENGTH 4095 + +/* N_PCI type values in bits 7-4 of N_PCI bytes */ +#define N_PCI_SF 0x00 /* single frame */ +#define N_PCI_FF 0x10 /* first frame */ +#define N_PCI_CF 0x20 /* consecutive frame */ +#define N_PCI_FC 0x30 /* flow control */ + +#define N_PCI_SZ 1 /* size of the PCI byte #1 */ +#define SF_PCI_SZ 1 /* size of SingleFrame PCI including 4 bit SF_DL */ +#define FF_PCI_SZ 2 /* size of FirstFrame PCI including 12 bit FF_DL */ +#define FC_CONTENT_SZ 3 /* flow control content size in byte (FS/BS/STmin) */ + +/* Flow Status given in FC frame */ +#define ISOTP_FC_CTS 0 /* clear to send */ +#define ISOTP_FC_WT 1 /* wait */ +#define ISOTP_FC_OVFLW 2 /* overflow */ + +static kernel_pid_t isotp_pid = KERNEL_PID_UNDEF; +static struct isotp *isotp_list = NULL; +static mutex_t lock = MUTEX_INIT; + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +static void _rx_timeout(void *arg); +static int _isotp_send_fc(struct isotp *isotp, int ae, uint8_t status); +static int _isotp_tx_send(struct isotp *isotp, struct can_frame *frame); + +static int _send_msg(msg_t *msg, can_reg_entry_t *entry) +{ +#ifdef MODULE_CAN_MBOX + switch (entry->type) { + case CAN_TYPE_DEFAULT: + return msg_try_send(msg, entry->target.pid); + case CAN_TYPE_MBOX: + DEBUG("_send_msg: sending msg=%p to mbox=%p\n", (void *)msg, (void *)entry->target.mbox); + return mbox_try_put(entry->target.mbox, msg); + default: + return -ENOTSUP; + } +#else + return msg_try_send(msg, entry->target.pid); +#endif +} + +static int _isotp_dispatch_rx(struct isotp *isotp) +{ + msg_t msg; + int ret = 0; + can_rx_data_t *data; + + msg.type = CAN_MSG_RX_INDICATION; + data = can_pkt_alloc_rx_data(isotp->rx.snip, + isotp->rx.snip->size + sizeof(*isotp->rx.snip), + isotp->arg); + + if (!data) { + return -ENOMEM; + } + + msg.content.ptr = data; + if (_send_msg(&msg, &isotp->entry) < 1) { + DEBUG("_isotp_dispatch_rx: msg lost, freeing rx buf\n"); + gnrc_pktbuf_release(((gnrc_pktsnip_t *)data->data.iov_base)); + can_pkt_free_rx_data(data); + ret = -EOVERFLOW; + } + + isotp->rx.snip = NULL; + + return ret; +} + +static int _isotp_dispatch_tx(struct isotp *isotp, int err) +{ + msg_t msg; + + gnrc_pktbuf_release(isotp->tx.snip); + isotp->tx.snip = NULL; + + if (isotp->opt.flags & CAN_ISOTP_TX_DONT_WAIT) { + return 0; + } + + if (!err) { + msg.type = CAN_MSG_TX_CONFIRMATION; + } + else { + msg.type = CAN_MSG_TX_ERROR; + } + + msg.content.ptr = isotp->arg; + + if (_send_msg(&msg, &isotp->entry) < 1) { + DEBUG("_isotp_dispatch_tx: msg lost\n"); + return -EOVERFLOW; + } + + return 0; +} + +static void _rx_timeout(void *arg) +{ + msg_t msg; + + DEBUG("_rx_timeout: arg=%p\n", arg); + + msg.type = CAN_MSG_ISOTP_RX_TIMEOUT; + msg.content.ptr = arg; + + msg_send(&msg, isotp_pid); +} + +static void _tx_timeout(void *arg) +{ + msg_t msg; + + DEBUG("_tx_timeout: arg=%p\n", arg); + + msg.type = CAN_MSG_ISOTP_TX_TIMEOUT; + msg.content.ptr = arg; + + msg_send(&msg, isotp_pid); +} + +static int _isotp_rcv_fc(struct isotp *isotp, struct can_frame *frame, int ae) +{ + if (isotp->tx.state != ISOTP_WAIT_FC) { + return 0; + } + + xtimer_remove(&isotp->tx_timer); + + if (frame->can_dlc < ae + FC_CONTENT_SZ) { + /* Invalid length */ + isotp->tx.state = ISOTP_IDLE; + return 1; + } + + isotp->txfc.bs = frame->data[ae + 1]; + isotp->txfc.stmin = frame->data[ae + 2]; + + DEBUG("_isotp_rcv_fc: first FC: bs=0x%" PRIx8 ", stmin=0x%" PRIx8 "\n", + isotp->txfc.bs, isotp->txfc.stmin); + + if ((isotp->txfc.stmin > 0x7F) && + ((isotp->txfc.stmin < 0xF1) || (isotp->txfc.stmin > 0xF9))) { + /* according to ISO15765-2 8.5.5.6 */ + isotp->txfc.stmin = 0x7F; + } + /* ISO15765-2 8.5.5.5 */ + /* Range 0x0 - 0x7F -> 0 ms - 127 ms */ + if (isotp->txfc.stmin < 0x80) { + isotp->tx_gap = isotp->txfc.stmin * US_PER_MS; + } + /* Range 0xF1 - 0xF9 -> 100 us - 900 us */ + else { + isotp->tx_gap = (isotp->txfc.stmin - 0xF0) * 100; + } + + switch (frame->data[ae] & 0xF) { + case ISOTP_FC_CTS: + isotp->tx_wft = 0; + isotp->tx.bs = 0; + isotp->tx.state = ISOTP_SENDING_NEXT_CF; + xtimer_set(&isotp->tx_timer, isotp->tx_gap); + break; + + case ISOTP_FC_WT: + if (isotp->tx_wft++ >= isotp->txfc.wftmax) { + isotp->tx.state = ISOTP_IDLE; + _isotp_dispatch_tx(isotp, ETIMEDOUT); + return 1; + } + /* BS and STmin shall be ignored */ + xtimer_set(&isotp->tx_timer, CAN_ISOTP_TIMEOUT_N_Bs); + break; + + case ISOTP_FC_OVFLW: + /* overflow on receiver side -> error */ + + default: + isotp->tx.state = ISOTP_IDLE; + _isotp_dispatch_tx(isotp, EOVERFLOW); + break; + } + + return 0; +} + +static int _isotp_rcv_sf(struct isotp *isotp, struct can_frame *frame, int ae) +{ + xtimer_remove(&isotp->rx_timer); + isotp->rx.state = ISOTP_IDLE; + + int len = (frame->data[ae] & 0x0F); + if (len > frame->can_dlc - (SF_PCI_SZ + ae)) { + return 1; + } + + gnrc_pktsnip_t *snip = gnrc_pktbuf_add(NULL, NULL, len, GNRC_NETTYPE_UNDEF); + if (!snip) { + return 1; + } + isotp->rx.snip = snip; + + isotp->rx.idx = 0; + for (size_t i = SF_PCI_SZ + ae; i < isotp->rx.snip->size + ae + SF_PCI_SZ; i++) { + ((uint8_t *)isotp->rx.snip->data)[isotp->rx.idx++] = frame->data[i]; + } + + return _isotp_dispatch_rx(isotp); +} + +static int _isotp_rcv_ff(struct isotp *isotp, struct can_frame *frame, int ae) +{ + isotp->rx.state = ISOTP_IDLE; + + int len = (frame->data[ae] & 0x0F) << 8; + len += frame->data[ae + 1]; + + if (isotp->rx.snip) { + DEBUG("_isotp_rcv_ff: freeing previous rx buf\n"); + gnrc_pktbuf_release(isotp->rx.snip); + } + + if (len > MAX_MSG_LENGTH) { + if (!(isotp->opt.flags & CAN_ISOTP_LISTEN_MODE)) { + _isotp_send_fc(isotp, ae, ISOTP_FC_OVFLW); + } + return 1; + } + + gnrc_pktsnip_t *snip = gnrc_pktbuf_add(NULL, NULL, len, GNRC_NETTYPE_UNDEF); + if (!snip) { + if (!(isotp->opt.flags & CAN_ISOTP_LISTEN_MODE)) { + _isotp_send_fc(isotp, ae, ISOTP_FC_OVFLW); + } + return 1; + } + isotp->rx.snip = snip; + + isotp->rx.idx = 0; + for (int i = ae + FF_PCI_SZ; i < frame->can_dlc; i++) { + ((uint8_t *)isotp->rx.snip->data)[isotp->rx.idx++] = frame->data[i]; + } + +#if ENABLE_DEBUG + DEBUG("_isotp_rcv_ff: rx.buf="); + for (unsigned i = 0; i < isotp->rx.idx; i++) { + DEBUG("%02hhx", ((uint8_t *)isotp->rx.snip->data)[i]); + } + DEBUG("\n"); +#endif + + isotp->rx.sn = 1; + + if (isotp->opt.flags & CAN_ISOTP_LISTEN_MODE) { + isotp->rx.state = ISOTP_WAIT_CF; + return 0; + } + + isotp->rx.state = ISOTP_SENDING_FC; + _isotp_send_fc(isotp, ae, ISOTP_FC_CTS); + + return 0; +} + +static int _isotp_rcv_cf(struct isotp *isotp, struct can_frame *frame, int ae) +{ + DEBUG("_isotp_rcv_cf: state=%d\n", isotp->rx.state); + + if (isotp->rx.state != ISOTP_WAIT_CF) { + return 1; + } + + xtimer_remove(&isotp->rx_timer); + + if ((frame->data[ae] & 0x0F) != isotp->rx.sn) { + DEBUG("_isotp_rcv_cf: wrong seq number %d, expected %d\n", frame->data[ae] & 0x0F, isotp->rx.sn); + isotp->rx.state = ISOTP_IDLE; + gnrc_pktbuf_release(isotp->rx.snip); + isotp->rx.snip = NULL; + return 1; + } + isotp->rx.sn++; + isotp->rx.sn %= 16; + + for (int i = ae + N_PCI_SZ; i < frame->can_dlc; i++) { + ((uint8_t *)isotp->rx.snip->data)[isotp->rx.idx++] = frame->data[i]; + if (isotp->rx.idx >= isotp->rx.snip->size) { + break; + } + } + +#if ENABLE_DEBUG + DEBUG("_isotp_rcv_cf: rx.buf="); + for (unsigned i = 0; i < isotp->rx.idx; i++) { + DEBUG("%02hhx", ((uint8_t *)isotp->rx.snip->data)[i]); + } + DEBUG("\n"); +#endif + + if (isotp->rx.idx >= isotp->rx.snip->size) { + isotp->rx.state = ISOTP_IDLE; + return _isotp_dispatch_rx(isotp); + } + + if (isotp->opt.flags & CAN_ISOTP_LISTEN_MODE) { + return 0; + } + + DEBUG("_isotp_rcv_cf: rxfc.bs=%" PRIx8 " rx.bs=%" PRIx8 "\n", isotp->rxfc.bs, isotp->rx.bs); + + if (!isotp->rxfc.bs || (++isotp->rx.bs < isotp->rxfc.bs)) { + xtimer_set(&isotp->rx_timer, CAN_ISOTP_TIMEOUT_N_Cr); + return 0; + } + + return _isotp_send_fc(isotp, ae, ISOTP_FC_CTS); +} + +static int _isotp_rcv(struct isotp *isotp, struct can_frame *frame) +{ + int ae = (isotp->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0; + uint8_t n_pci_type; + +#if ENABLE_DEBUG + DEBUG("_isotp_rcv: id=%" PRIx32 " data=", frame->can_id); + for (int i = 0; i < frame->can_dlc; i++) { + DEBUG("%02hhx", frame->data[i]); + } + DEBUG("\n"); +#endif + + if (ae && frame->data[0] != isotp->opt.rx_ext_address) { + return 1; + } + + n_pci_type = frame->data[ae] & 0xF0; + + switch (n_pci_type) { + case N_PCI_FC: + return _isotp_rcv_fc(isotp, frame, ae); + + case N_PCI_SF: + return _isotp_rcv_sf(isotp, frame, ae); + + case N_PCI_FF: + return _isotp_rcv_ff(isotp, frame, ae); + + case N_PCI_CF: + return _isotp_rcv_cf(isotp, frame, ae); + + } + + return 1; +} + +static int _isotp_send_fc(struct isotp *isotp, int ae, uint8_t status) +{ + struct can_frame fc; + + fc.can_id = isotp->opt.tx_id; + + if (isotp->opt.flags & CAN_ISOTP_TX_PADDING) { + memset(fc.data, isotp->opt.txpad_content, CAN_MAX_DLEN); + fc.can_dlc = CAN_MAX_DLEN; + } + else { + fc.can_dlc = ae + FC_CONTENT_SZ; + } + + fc.data[ae] = N_PCI_FC | status; + fc.data[ae + 1] = isotp->rxfc.bs; + fc.data[ae + 2] = isotp->rxfc.stmin; + + if (ae) { + fc.data[0] = isotp->opt.ext_address; + } + + isotp->rx.bs = 0; + +#if ENABLE_DEBUG + DEBUG("_isotp_send_fc: id=%" PRIx32 " data=", fc.can_id); + for (int i = 0; i < fc.can_dlc; i++) { + DEBUG("%02hhx", fc.data[i]); + } + DEBUG("\n"); +#endif + + xtimer_set(&isotp->rx_timer, CAN_ISOTP_TIMEOUT_N_Ar); + isotp->rx.tx_handle = raw_can_send(isotp->entry.ifnum, &fc, isotp_pid); + + if (isotp->rx.tx_handle >= 0) { + return 0; + } + else { + isotp->rx.state = ISOTP_IDLE; + xtimer_remove(&isotp->rx_timer); + return isotp->rx.tx_handle; + } +} + +static void _isotp_create_ff(struct isotp *isotp, struct can_frame *frame, int ae) +{ + + frame->can_id = isotp->opt.tx_id; + frame->can_dlc = CAN_MAX_DLEN; + + if (ae) { + frame->data[0] = isotp->opt.ext_address; + } + + frame->data[ae] = (uint8_t)(isotp->tx.snip->size >> 8) | N_PCI_FF; + frame->data[ae + 1] = (uint8_t) isotp->tx.snip->size & 0xFFU; + + for (int i = ae + FF_PCI_SZ; i < CAN_MAX_DLEN; i++) { + frame->data[i] = ((uint8_t *)isotp->tx.snip->data)[isotp->tx.idx++]; + } + + isotp->tx.sn = 1; +} + +static void _isotp_fill_dataframe(struct isotp *isotp, struct can_frame *frame, int ae) +{ + size_t pci_len = N_PCI_SZ + ae; + size_t space = CAN_MAX_DLEN - pci_len; + size_t num_bytes = MIN(space, isotp->tx.snip->size - isotp->tx.idx); + + frame->can_id = isotp->opt.tx_id; + frame->can_dlc = num_bytes + pci_len; + + DEBUG("_isotp_fill_dataframe: num_bytes=%d, pci_len=%d\n", num_bytes, pci_len); + + if (num_bytes < space) { + if (isotp->opt.flags & CAN_ISOTP_TX_PADDING) { + frame->can_dlc = CAN_MAX_DLEN; + memset(frame->data, isotp->opt.txpad_content, frame->can_dlc); + } + } + + for (size_t i = 0; i < num_bytes; i++) { + frame->data[pci_len + i] = ((uint8_t *)isotp->tx.snip->data)[isotp->tx.idx++]; + } + + if (ae) { + frame->data[0] = isotp->opt.ext_address; + } + +} + +static void _isotp_tx_timeout_task(struct isotp *isotp) +{ + int ae = (isotp->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0; + struct can_frame frame; + + DEBUG("_isotp_tx_timeout_task: state=%d\n", isotp->tx.state); + + switch (isotp->tx.state) { + case ISOTP_WAIT_FC: + DEBUG("_isotp_tx_timeout_task: FC not received on time\n"); + isotp->tx.state = ISOTP_IDLE; + _isotp_dispatch_tx(isotp, ETIMEDOUT); + break; + + case ISOTP_SENDING_NEXT_CF: + DEBUG("_isotp_tx_timeout_task: sending next CF\n"); + _isotp_fill_dataframe(isotp, &frame, ae); + frame.data[ae] = N_PCI_CF | isotp->tx.sn++; + isotp->tx.sn %= 16; + isotp->tx.bs++; + + isotp->tx.state = ISOTP_SENDING_CF; + _isotp_tx_send(isotp, &frame); + break; + + case ISOTP_SENDING_CF: + case ISOTP_SENDING_FF: + case ISOTP_SENDING_SF: + DEBUG("_isotp_tx_timeout_task: timeout on DLL\n"); + isotp->tx.state = ISOTP_IDLE; + raw_can_abort(isotp->entry.ifnum, isotp->tx.tx_handle); + _isotp_dispatch_tx(isotp, ETIMEDOUT); + break; + } +} + +static void _isotp_tx_tx_conf(struct isotp *isotp) +{ + xtimer_remove(&isotp->tx_timer); + isotp->tx.tx_handle = 0; + + DEBUG("_isotp_tx_tx_conf: state=%d\n", isotp->tx.state); + + switch (isotp->tx.state) { + case ISOTP_SENDING_SF: + isotp->tx.state = ISOTP_IDLE; + _isotp_dispatch_tx(isotp, 0); + break; + + case ISOTP_SENDING_FF: + isotp->tx.state = ISOTP_WAIT_FC; + xtimer_set(&isotp->tx_timer, CAN_ISOTP_TIMEOUT_N_Bs); + break; + + case ISOTP_SENDING_CF: + if (isotp->tx.idx >= isotp->tx.snip->size) { + /* Finished */ + isotp->tx.state = ISOTP_IDLE; + _isotp_dispatch_tx(isotp, 0); + break; + } + + if (isotp->txfc.bs && (isotp->tx.bs >= isotp->txfc.bs)) { + /* wait for FC */ + isotp->tx.state = ISOTP_WAIT_FC; + xtimer_set(&isotp->tx_timer, CAN_ISOTP_TIMEOUT_N_Bs); + break; + } + + isotp->tx.state = ISOTP_SENDING_NEXT_CF; + xtimer_set(&isotp->tx_timer, isotp->tx_gap); + break; + } +} + +static void _isotp_rx_timeout_task(struct isotp *isotp) +{ + switch (isotp->rx.state) { + case ISOTP_SENDING_FC: + DEBUG("_isotp_rx_timeout_task: FC tx conf timeout\n"); + raw_can_abort(isotp->entry.ifnum, isotp->rx.tx_handle); + case ISOTP_WAIT_CF: + DEBUG("_isotp_rx_timeout_task: free rx buf\n"); + gnrc_pktbuf_release(isotp->rx.snip); + isotp->rx.snip = NULL; + isotp->rx.state = ISOTP_IDLE; + /* TODO dispatch rx error ? */ + break; + } +} + +static void _isotp_rx_tx_conf(struct isotp *isotp) +{ + xtimer_remove(&isotp->rx_timer); + isotp->rx.tx_handle = 0; + + DEBUG("_isotp_rx_tx_conf: state=%d\n", isotp->rx.state); + + switch (isotp->rx.state) { + case ISOTP_SENDING_FC: + isotp->rx.state = ISOTP_WAIT_CF; + xtimer_set(&isotp->rx_timer, CAN_ISOTP_TIMEOUT_N_Cr); + break; + } +} + +static int _isotp_tx_send(struct isotp *isotp, struct can_frame *frame) +{ + xtimer_set(&isotp->tx_timer, CAN_ISOTP_TIMEOUT_N_As); + isotp->tx.tx_handle = raw_can_send(isotp->entry.ifnum, frame, isotp_pid); + DEBUG("isotp_send: FF/SF/CF sent handle=%d\n", isotp->tx.tx_handle); + if (isotp->tx.tx_handle < 0) { + xtimer_remove(&isotp->tx_timer); + isotp->tx.state = ISOTP_IDLE; + return _isotp_dispatch_tx(isotp, isotp->tx.tx_handle); + } + + return 0; +} + +static int _isotp_send_sf_ff(struct isotp *isotp) +{ + struct can_frame frame; + unsigned ae = (isotp->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0; + + if (isotp->tx.snip->size <= CAN_MAX_DLEN - SF_PCI_SZ - ae) { + /* Fits into a single frame */ + _isotp_fill_dataframe(isotp, &frame, ae); + + frame.data[ae] = N_PCI_SF; + frame.data[ae] |= isotp->tx.snip->size; + + isotp->tx.state = ISOTP_SENDING_SF; + } + else { + isotp->tx.state = ISOTP_SENDING_FF; + /* Must send a First frame */ + _isotp_create_ff(isotp, &frame, ae); + } + + return _isotp_tx_send(isotp, &frame); +} + +static void *_isotp_thread(void *args) +{ + (void)args; + msg_t msg, msg_queue[CAN_ISOTP_MSG_QUEUE_SIZE]; + struct can_rx_data *rx_frame; + struct isotp *isotp; + + /* setup the device layers message queue */ + msg_init_queue(msg_queue, CAN_ISOTP_MSG_QUEUE_SIZE); + + isotp_pid = sched_active_pid; + + while (1) { + msg_receive(&msg); + switch (msg.type) { + case CAN_MSG_SEND_FRAME: + _isotp_send_sf_ff(msg.content.ptr); + break; + case CAN_MSG_RX_INDICATION: + rx_frame = msg.content.ptr; + if (!rx_frame) { + DEBUG("_isotp_thread: CAN_MSG_RX_INDICATION with NULL ptr\n"); + break; + } + DEBUG("_isotp_thread: CAN_MSG_RX_INDICATION, frame=%p, data=%p\n", + (void *)rx_frame->data.iov_base, rx_frame->arg); + _isotp_rcv((struct isotp *)rx_frame->arg, rx_frame->data.iov_base); + raw_can_free_frame(rx_frame); + break; + case CAN_MSG_TX_CONFIRMATION: + DEBUG("_isotp_thread: CAN_MSG_TX_CONFIRMATION, handle=%d\n", (int)msg.content.value); + mutex_lock(&lock); + LL_FOREACH(isotp_list, isotp) { + if (isotp->tx.tx_handle == (int)msg.content.value) { + mutex_unlock(&lock); + _isotp_tx_tx_conf(isotp); + break; + } + else if (isotp->rx.tx_handle == (int)msg.content.value) { + mutex_unlock(&lock); + _isotp_rx_tx_conf(isotp); + break; + } + } + if (isotp == NULL) { + mutex_unlock(&lock); + } + break; + case CAN_MSG_ISOTP_RX_TIMEOUT: + isotp = msg.content.ptr; + DEBUG("_isotp_thread: RX TIMEOUT arg=%p\n", (void *)isotp); + _isotp_rx_timeout_task(isotp); + break; + case CAN_MSG_ISOTP_TX_TIMEOUT: + isotp = msg.content.ptr; + DEBUG("_isotp_thread: TX_TIMEOUT arg=%p\n", (void *)isotp); + _isotp_tx_timeout_task(isotp); + break; + } + } + + return NULL; +} + +kernel_pid_t isotp_init(char *stack, int stacksize, char priority, const char *name) +{ + kernel_pid_t res; + + DEBUG("isotp_init\n"); + + /* create new can device thread */ + res = thread_create(stack, stacksize, priority, THREAD_CREATE_STACKTEST, + _isotp_thread, NULL, name); + if (res <= 0) { + return -EINVAL; + } + + return res; +} + +int isotp_send(struct isotp *isotp, const void *buf, int len, int flags) +{ + assert(isotp != NULL); +#ifdef MODULE_CAN_MBOX + assert((isotp->entry.type == CAN_TYPE_DEFAULT && pid_is_valid(isotp->entry.target.pid)) || + (isotp->entry.type == CAN_TYPE_MBOX && isotp->entry.target.mbox != NULL)); +#else + assert(isotp->entry.target.pid != KERNEL_PID_UNDEF); +#endif + assert (len && len <= MAX_MSG_LENGTH); + + if (isotp->tx.state != ISOTP_IDLE) { + return -EBUSY; + } + + if (flags) { + isotp->opt.flags &= CAN_ISOTP_RX_FLAGS_MASK; + isotp->opt.flags |= (flags & CAN_ISOTP_TX_FLAGS_MASK); + } + + gnrc_pktsnip_t *snip = gnrc_pktbuf_add(NULL, NULL, len, GNRC_NETTYPE_UNDEF); + if (!snip) { + return -ENOMEM; + } + isotp->tx.snip = snip; + + memcpy(isotp->tx.snip->data, buf, len); + + isotp->tx.idx = 0; + + isotp->tx_wft = 0; + + msg_t msg; + msg.type = CAN_MSG_SEND_FRAME; + msg.content.ptr = isotp; + msg_send(&msg, isotp_pid); + + return len; +} + +int isotp_bind(struct isotp *isotp, can_reg_entry_t *entry, void *arg) +{ + int ret; + + assert(isotp != NULL); +#ifdef MODULE_CAN_MBOX + assert((entry->type == CAN_TYPE_DEFAULT && pid_is_valid(entry->target.pid)) || + (entry->type == CAN_TYPE_MBOX && entry->target.mbox != NULL)); +#else + assert(pid_is_valid(entry->target.pid)); +#endif + assert(isotp->opt.tx_id != isotp->opt.rx_id); + assert(!((isotp->opt.tx_id | isotp->opt.rx_id) & (CAN_RTR_FLAG | CAN_ERR_FLAG))); + assert(entry->ifnum < CAN_DLL_NUMOF); + + isotp->rx_timer.callback = _rx_timeout; + isotp->rx_timer.arg = isotp; + + isotp->tx_timer.callback = _tx_timeout; + isotp->tx_timer.arg = isotp; + + memset(&isotp->rx, 0, sizeof(struct tpcon)); + memset(&isotp->tx, 0, sizeof(struct tpcon)); + + isotp->rxfc.bs = CAN_ISOTP_BS; + isotp->rxfc.stmin = CAN_ISOTP_STMIN; + isotp->rxfc.wftmax = 0; + + isotp->txfc.bs = 0; + isotp->txfc.stmin = 0; + isotp->txfc.wftmax = CAN_ISOTP_WFTMAX; + + isotp->entry.ifnum = entry->ifnum; +#ifdef MODULE_CAN_MBOX + isotp->entry.type = entry->type; + isotp->entry.target.mbox = entry->target.mbox; +#else + isotp->entry.target.pid = entry->target.pid; +#endif + isotp->arg = arg; + isotp->next = NULL; + + DEBUG("isotp_bind: ifnum=%d, txid=%" PRIx32 ", rxid=%" PRIx32 ", flags=0x%" PRIx16 "\n", + isotp->entry.ifnum, isotp->opt.tx_id, isotp->opt.rx_id, isotp->opt.flags); + DEBUG("isotp_bind: pid=%" PRIkernel_pid "\n", entry->target.pid); + + struct can_filter filter = { + .can_id = isotp->opt.rx_id, + .can_mask = 0xFFFFFFFF, + }; + ret = raw_can_subscribe_rx(isotp->entry.ifnum, &filter, isotp_pid, isotp); + if (ret < 0) { + return ret; + } + + mutex_lock(&lock); + LL_APPEND(isotp_list, isotp); + mutex_unlock(&lock); + + return 0; +} + +void isotp_free_rx(can_rx_data_t *rx) +{ + DEBUG("isotp_free_rx: rx=%p\n", (void *)rx); + gnrc_pktbuf_release(rx->data.iov_base); + can_pkt_free_rx_data(rx); +} + +int isotp_release(struct isotp *isotp) +{ + assert(isotp != NULL); +#ifdef MODULE_CAN_MBOX + assert((isotp->entry.type == CAN_TYPE_DEFAULT && pid_is_valid(isotp->entry.target.pid)) || + (isotp->entry.type == CAN_TYPE_MBOX && isotp->entry.target.mbox != NULL)); +#else + assert(isotp->entry.target.pid != KERNEL_PID_UNDEF); +#endif + + DEBUG("isotp_release: isotp=%p\n", (void *)isotp); + + struct can_filter filter = { + .can_id = isotp->opt.rx_id, + .can_mask = 0xFFFFFFFF, + }; + raw_can_unsubscribe_rx(isotp->entry.ifnum, &filter, isotp_pid, isotp); + + if (isotp->rx.snip) { + DEBUG("isotp_release: freeing rx buf\n"); + gnrc_pktbuf_release(isotp->rx.snip); + isotp->rx.snip = NULL; + } + isotp->rx.state = ISOTP_IDLE; + isotp->entry.target.pid = KERNEL_PID_UNDEF; + + mutex_lock(&lock); + LL_DELETE(isotp_list, isotp); + mutex_unlock(&lock); + + if (isotp->tx.snip) { + DEBUG("isotp_release: freeing rx buf\n"); + gnrc_pktbuf_release(isotp->tx.snip); + isotp->tx.snip = NULL; + } + isotp->tx.state = ISOTP_IDLE; + + return 0; +} diff --git a/sys/include/can/isotp.h b/sys/include/can/isotp.h new file mode 100644 index 000000000..2fe456385 --- /dev/null +++ b/sys/include/can/isotp.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup can + * @defgroup isotp ISOTP + * @brief ISO transport protocol over CAN (ISO15765) + * @{ + * + * + * @file + * @brief ISO TP high level interface + * + * @author Vincent Dupont + */ + +#ifndef CAN_ISOTP_H +#define CAN_ISOTP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "can/can.h" +#include "can/common.h" +#include "thread.h" +#include "xtimer.h" +#include "net/gnrc/pktbuf.h" + + +/** + * @brief The isotp_fc_options struct + * + * It describes the flow control options + */ +struct isotp_fc_options { + uint8_t bs; /**< blocksize provided in FC frame, 0 = off */ + + /** separation time provided in FC frame + * 0x00 - 0x7F : 0 - 127 ms + * 0x80 - 0xF0 : reserved + * 0xF1 - 0xF9 : 100 us - 900 us + * 0xFA - 0xFF : reserved */ + uint8_t stmin; + + uint8_t wftmax; /**< max. number of wait frame transmiss., 0 = ignored */ +}; + +/** + * @brief The isotp_options struct + * + * It describes the ISO-TP options + */ +struct isotp_options { + canid_t tx_id; /**< transmit CAN ID */ + canid_t rx_id; /**< Receive CAN ID */ + uint16_t flags; /**< set flags for isotp behaviour. */ + uint8_t ext_address; /**< set address for extended addressing */ + uint8_t txpad_content; /**< set content of padding byte (tx) */ + uint8_t rx_ext_address; /**< set address for extended addressing */ +}; + +/** + * @brief The tpcon struct + * + * It describes the current connection status + */ +struct tpcon { + unsigned idx; /**< current index in @p buf */ + uint8_t state; /**< the protocol state */ + uint8_t bs; /**< block size */ + uint8_t sn; /**< current sequence number */ + int tx_handle; /**< handle of the last sent frame */ + gnrc_pktsnip_t *snip; /**< allocated snip containing data buffer */ +}; + +/** + * @brief The isotp struct + * + * This is the main struct used by an ISO-TP channel + */ +struct isotp { + struct isotp *next; /**< next bound channel */ + struct isotp_options opt; /**< channel options */ + struct isotp_fc_options rxfc; /**< rx flow control options (defined locally) */ + struct isotp_fc_options txfc; /**< tx flow control options (defined remotely) */ + struct tpcon tx; /**< transmit state */ + struct tpcon rx; /**< receive state */ + xtimer_t tx_timer; /**< timer for tx operations */ + xtimer_t rx_timer; /**< timer for rx operations */ + can_reg_entry_t entry; /**< entry containing ifnum and upper layer msg system */ + uint32_t tx_gap; /**< transmit gap from fc (in us) */ + uint8_t tx_wft; /**< transmit wait counter */ + void *arg; /**< upper layer private arg */ +}; + +/** + * @name flags for isotp behaviour + * @{ + */ +#define CAN_ISOTP_RX_FLAGS_MASK 0x0000FFFF /**< rx flags mask */ +#define CAN_ISOTP_LISTEN_MODE 0x0001 /**< listen only flag (do not send FC) */ +#define CAN_ISOTP_EXTEND_ADDR 0x0002 /**< enable extended addressing */ +#define CAN_ISOTP_TX_PADDING 0x0004 /**< enable CAN frame padding tx path */ +#define CAN_ISOTP_HALF_DUPLEX 0x0040 /**< half duplex error state handling */ +#define CAN_ISOTP_RX_EXT_ADDR 0x0200 /**< different rx extended addressing */ + +#define CAN_ISOTP_TX_FLAGS_MASK 0xFFFF0000 /**< tx flags mask */ +#define CAN_ISOTP_TX_DONT_WAIT 0x00010000 /**< do not send a tx confirmation msg */ +/** @} */ + +/** + * @name default configuration values + * @{ + */ +#define CAN_ISOTP_DEFAULT_FLAGS 0 +#define CAN_ISOTP_DEFAULT_EXT_ADDRESS 0x00 +#define CAN_ISOTP_DEFAULT_PAD_CONTENT 0xCC /* prevent bit-stuffing */ +#define CAN_ISOTP_DEFAULT_FRAME_TXTIME 0 +#define CAN_ISOTP_DEFAULT_RECV_BS 0 +#define CAN_ISOTP_DEFAULT_RECV_STMIN 0x00 +#define CAN_ISOTP_DEFAULT_RECV_WFTMAX 0 +/** @} */ + +/** + * @brief Initialize the isotp layer + * + * @param stack stack for the isotp thread + * @param stacksize size of @p stack + * @param priority priority of the isotp thread + * @param name name of the isotp thread + * + * @return the pid of the isotp thread + */ +kernel_pid_t isotp_init(char *stack, int stacksize, char priority, const char *name); + +/** + * @brief Send data through an isotp channel + * + * @param isotp the channel to use + * @param buf the data to send + * @param len length of the data to send + * @param flags flags for sending + * + * @return the number of bytes sent + * @return < 0 if an error occured (-EBUSY, -ENOMEM) + */ +int isotp_send(struct isotp *isotp, const void *buf, int len, int flags); + +/** + * @brief Bind an isotp channel + * + * Initialize the channel, set the filter on the DLL and add the + * channel to the list of bound channels + * + * @param isotp the channel to bind + * @param entry entry identifying the CAN ifnum and the upper layer + * either by its pid or its mailbox + * @param arg upper layer private parameter + * + * @return 0 on success, < 0 on error + */ +int isotp_bind(struct isotp *isotp, can_reg_entry_t *entry, void *arg); + +/** + * @brief Release a bound isotp channel + * + * Unset the filter on the DLL and remove the channel from the list + * of bound channels + * + * @param isotp the channel to relase + * + * @return 0 on success, < 0 on error + */ +int isotp_release(struct isotp *isotp); + +/** + * @brief Free a received buffer + * + * This MUST be called by the upper layer when the received data are read + * + * @param rx the received data + */ +void isotp_free_rx(can_rx_data_t *rx); + +#ifdef __cplusplus +} +#endif + +#endif /* CAN_ISOTP_H */ +/** @} */ From ab5f8548f2574b5057af314caa2b4111eed5bb14 Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Wed, 23 Nov 2016 19:10:34 +0100 Subject: [PATCH 5/9] can: add conn_can top layer interface This module provide support for raw can and isotp socket-like user interface. --- Makefile.dep | 6 + makefiles/pseudomodules.inc.mk | 1 + sys/can/Makefile | 4 + sys/can/conn/Makefile | 3 + sys/can/conn/isotp.c | 419 +++++++++++++++++++++++++++++++++ sys/can/conn/raw.c | 279 ++++++++++++++++++++++ sys/include/can/conn/isotp.h | 202 ++++++++++++++++ sys/include/can/conn/raw.h | 138 +++++++++++ 8 files changed, 1052 insertions(+) create mode 100644 sys/can/conn/Makefile create mode 100644 sys/can/conn/isotp.c create mode 100644 sys/can/conn/raw.c create mode 100644 sys/include/can/conn/isotp.h create mode 100644 sys/include/can/conn/raw.h diff --git a/Makefile.dep b/Makefile.dep index d4706ae62..43dec2a26 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -583,6 +583,12 @@ ifneq (,$(filter can_isotp,$(USEMODULE))) USEMODULE += xtimer endif +ifneq (,$(filter conn_can,$(USEMODULE))) + USEMODULE += can + USEMODULE += can_mbox + USEMODULE += xtimer +endif + ifneq (,$(filter random,$(USEMODULE))) # select default prng ifeq (,$(filter prng_%,$(USEMODULE))) diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 003fad819..83a3d62ae 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -2,6 +2,7 @@ PSEUDOMODULES += auto_init_gnrc_rpl PSEUDOMODULES += can_mbox PSEUDOMODULES += can_pm PSEUDOMODULES += can_raw +PSEUDOMODULES += conn_can_isotp_multi PSEUDOMODULES += core_% PSEUDOMODULES += emb6_router PSEUDOMODULES += gnrc_ipv6_default diff --git a/sys/can/Makefile b/sys/can/Makefile index 449d4878c..f6f3dc0f4 100644 --- a/sys/can/Makefile +++ b/sys/can/Makefile @@ -3,4 +3,8 @@ ifneq (,$(filter can_isotp,$(USEMODULE))) DIRS += isotp endif +ifneq (,$(filter conn_can,$(USEMODULE))) + DIRS += conn +endif + include $(RIOTBASE)/Makefile.base diff --git a/sys/can/conn/Makefile b/sys/can/conn/Makefile new file mode 100644 index 000000000..22c56214a --- /dev/null +++ b/sys/can/conn/Makefile @@ -0,0 +1,3 @@ +MODULE = conn_can + +include $(RIOTBASE)/Makefile.base diff --git a/sys/can/conn/isotp.c b/sys/can/conn/isotp.c new file mode 100644 index 000000000..16b732827 --- /dev/null +++ b/sys/can/conn/isotp.c @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @file + * @brief Implementation of isotp CAN connection + * + * @author Vincent Dupont + */ + +#ifdef MODULE_CAN_ISOTP +#include +#include + +#include "can/conn/isotp.h" +#include "can/isotp.h" +#include "can/device.h" + +#ifdef MODULE_CONN_CAN_ISOTP_MULTI +#include "utlist.h" +#endif + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include "xtimer.h" + +#define _TIMEOUT_TX_MSG_TYPE (0x8000) +#define _TIMEOUT_RX_MSG_TYPE (0x8001) +#define _CLOSE_CONN_MSG_TYPE (0x8002) +#define _TIMEOUT_MSG_VALUE (0xABCDEFAB) + +#ifndef CONN_CAN_ISOTP_TIMEOUT_TX_CONF +#define CONN_CAN_ISOTP_TIMEOUT_TX_CONF (10 * US_PER_SEC) +#endif + +static inline void put_msg(conn_can_isotp_t *conn, msg_t *msg) +{ +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + mbox_put(&conn->master->mbox, msg); +#else + mbox_put(&conn->mbox, msg); +#endif +} + +static inline void get_msg(conn_can_isotp_t *conn, msg_t *msg) +{ +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + mbox_get(&conn->master->mbox, msg); +#else + mbox_get(&conn->mbox, msg); +#endif +} + +int conn_can_isotp_create(conn_can_isotp_t *conn, struct isotp_options *options, int ifnum) +{ + assert(conn != NULL); + assert(options != NULL); + assert(ifnum < CAN_DLL_NUMOF); + +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + DEBUG("conn_can_isotp_create: conn=%p, conn->master=%p, ifnum=%d\n", + (void *)conn, (void *)conn->master, ifnum); + + if (conn->master == conn || conn->master == NULL) { + conn->master = conn; + conn->master->next = NULL; + mutex_init(&conn->master->lock); + mutex_lock(&conn->master->lock); + DEBUG("conn_can_isotp_create: init master conn\n"); + mbox_init(&conn->master->mbox, conn->master->mbox_queue, CONN_CAN_ISOTP_MBOX_SIZE); + mutex_unlock(&conn->master->lock); + } +#else + mbox_init(&conn->mbox, conn->mbox_queue, CONN_CAN_ISOTP_MBOX_SIZE); +#endif + + conn->ifnum = ifnum; + + memset(&conn->isotp, 0, sizeof(struct isotp)); + conn->isotp.opt = *options; + + return 0; +} + +int conn_can_isotp_bind(conn_can_isotp_t *conn) +{ + assert(conn != NULL); + assert(conn->isotp.opt.tx_id != 0 || conn->isotp.opt.rx_id != 0); + + DEBUG("conn_can_isotp_bind: conn=%p, ifnum=%d\n", + (void *)conn, conn->ifnum); + + if (conn->bound) { + return -EALREADY; + } + msg_t msg; + int ret; + can_reg_entry_t entry; + entry.ifnum = conn->ifnum; + entry.type = CAN_TYPE_MBOX; +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + assert(conn->master != NULL); + + entry.target.mbox = &(conn->master->mbox); + if (conn != conn->master) { + mutex_lock(&conn->master->lock); + LL_APPEND(conn->master->next, (conn_can_isotp_slave_t *)conn); + mutex_unlock(&conn->master->lock); + } + ret = mbox_try_get(&conn->master->mbox, &msg); +#else + entry.target.mbox = &conn->mbox; + ret = mbox_try_get(&conn->mbox, &msg); +#endif + if ((ret == 1) && (msg.type != _CLOSE_CONN_MSG_TYPE)) { + DEBUG("conn_can_isotp_bind: msg in queue type=%x\n", msg.type); + put_msg(conn, &msg); + } + + ret = isotp_bind(&conn->isotp, &entry, conn); + if (!ret) { + conn->bound = 1; + } + return ret; +} + +static void _tx_conf_timeout(void *arg) +{ + conn_can_isotp_t *conn = arg; + msg_t msg; + + msg.type = _TIMEOUT_TX_MSG_TYPE; + msg.content.value = _TIMEOUT_MSG_VALUE; + + put_msg(conn, &msg); +} + +int conn_can_isotp_send(conn_can_isotp_t *conn, const void *buf, size_t size, int flags) +{ + assert(conn != NULL); + assert(buf != NULL || size == 0); + + int ret = 0; + + if (!conn->bound) { + return -ENOTCONN; + } + + if (flags & CAN_ISOTP_TX_DONT_WAIT) { + return isotp_send(&conn->isotp, buf, size, flags); + } + else { + xtimer_t timer; + timer.callback = _tx_conf_timeout; + timer.arg = conn; + xtimer_set(&timer, CONN_CAN_ISOTP_TIMEOUT_TX_CONF); + + ret = isotp_send(&conn->isotp, buf, size, flags); + + msg_t msg; + while (1) { + get_msg(conn, &msg); + switch (msg.type) { + case CAN_MSG_TX_ERROR: + if (msg.content.ptr == conn) { + ret = -EIO; + } + /* No break */ + case CAN_MSG_TX_CONFIRMATION: +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + if (msg.content.ptr != conn) { + mbox_put(&conn->master->mbox, &msg); + break; + } +#endif + xtimer_remove(&timer); + return ret; + case _TIMEOUT_TX_MSG_TYPE: + return -ETIMEDOUT; + default: + DEBUG("conn_can_isotp_send: unexpected msg %x, requeing\n", msg.type); + put_msg(conn, &msg); + break; + } + } + } + + return ret; +} + +static void _rx_timeout(void *arg) +{ + conn_can_isotp_t *conn = arg; + msg_t msg; + + msg.type = _TIMEOUT_RX_MSG_TYPE; + msg.content.value = _TIMEOUT_MSG_VALUE; + + put_msg(conn, &msg); +} + +int conn_can_isotp_recv(conn_can_isotp_t *conn, void *buf, size_t size, uint32_t timeout) +{ + assert(conn != NULL); + assert(buf != NULL); + + int ret = 0; + gnrc_pktsnip_t *snip; + + if (!conn->bound) { + return -ENOTCONN; + } + +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + if (conn->rx) { + snip = conn->rx->data.iov_base; + if (snip->size <= size) { + memcpy(buf, snip->data, snip->size); + ret = snip->size; + } + else { + ret = -EOVERFLOW; + } + isotp_free_rx(conn->rx); + conn->rx = NULL; + return ret; + } +#endif + + xtimer_t timer; + if (timeout != 0) { + timer.callback = _rx_timeout; + timer.arg = conn; + xtimer_set(&timer, timeout); + } + + msg_t msg; + can_rx_data_t *rx; + + while (1) { + get_msg(conn, &msg); + switch (msg.type) { + case CAN_MSG_RX_INDICATION: + DEBUG("conn_can_isotp_recv: CAN_MSG_RX_INDICATION\n"); + rx = msg.content.ptr; + snip = rx->data.iov_base; +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + if (rx->arg != conn) { + mbox_put(&conn->master->mbox, &msg); + break; + } +#endif + if (timeout != 0) { + xtimer_remove(&timer); + } + if (snip->size <= size) { + memcpy(buf, snip->data, snip->size); + ret = snip->size; + } + else { + ret = -EOVERFLOW; + } + isotp_free_rx(rx); + return ret; + case _TIMEOUT_RX_MSG_TYPE: + DEBUG("conn_can_isotp_recv: _TIMEOUT_RX_MSG_TYPE\n"); + if (msg.content.value == _TIMEOUT_MSG_VALUE) { + ret = -ETIMEDOUT; + } + else { + ret = -EINTR; + } + return ret; + case _CLOSE_CONN_MSG_TYPE: + DEBUG("conn_can_isotp_recv: _CLOSE_CONN_MSG_TYPE\n"); +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + if ((msg.content.ptr == conn) || (msg.content.ptr == conn->master)) { +#endif + if (timeout != 0) { + xtimer_remove(&timer); + } + return -ECONNABORTED; +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + } +#endif + break; + default: + DEBUG("conn_can_isotp_recv: unexpected msg %x\n", msg.type); + if (timeout != 0) { + xtimer_remove(&timer); + } + ret = -EINTR; + return ret; + } + } + + return ret; +} + +int conn_can_isotp_close(conn_can_isotp_t *conn) +{ + assert(conn != NULL); + msg_t msg; + + DEBUG("conn_can_isotp_close: conn=%p, ifnum=%d\n", + (void *)conn, conn->ifnum); + + if (!conn->bound) { + return -EALREADY; + } + +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + assert(conn->master != NULL); + + if (conn->master != conn) { + mutex_lock(&conn->master->lock); + LL_DELETE(conn->master->next, (conn_can_isotp_slave_t *)conn); + mutex_unlock(&conn->master->lock); + } + else { + if (conn->master->next) { + return -EBUSY; + } + } +#endif + + isotp_release(&conn->isotp); + +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + if (conn->rx) { + isotp_free_rx(conn->rx); + } + if (conn->master == conn) { + while (mbox_try_get(&conn->master->mbox, &msg)) { + if (msg.type == CAN_MSG_RX_INDICATION) { + DEBUG("conn_can_isotp_close: freeing %p\n", msg.content.ptr); + isotp_free_rx(msg.content.ptr); + } + } + } +#else + while (mbox_try_get(&conn->mbox, &msg)) { + if (msg.type == CAN_MSG_RX_INDICATION) { + DEBUG("conn_can_isotp_close: freeing %p\n", msg.content.ptr); + isotp_free_rx(msg.content.ptr); + } + } +#endif + + msg.type = _CLOSE_CONN_MSG_TYPE; + msg.content.ptr = conn; + put_msg(conn, &msg); + + conn->bound = 0; + + return 0; +} + +#ifdef MODULE_CONN_CAN_ISOTP_MULTI +int conn_can_isotp_select(conn_can_isotp_slave_t **conn, conn_can_isotp_t *master, uint32_t timeout) +{ + assert(master != NULL); + assert(conn != NULL); + + int ret; + + xtimer_t timer; + if (timeout != 0) { + timer.callback = _rx_timeout; + timer.arg = master; + xtimer_set(&timer, timeout); + } + + msg_t msg; + can_rx_data_t *rx; + + mbox_get(&master->mbox, &msg); + + if (timeout != 0) { + xtimer_remove(&timer); + } + switch (msg.type) { + case CAN_MSG_RX_INDICATION: + DEBUG("conn_can_isotp_select: CAN_MSG_RX_INDICATION\n"); + rx = msg.content.ptr; + *conn = rx->arg; + (*conn)->rx = rx; + ret = 0; + break; + case _TIMEOUT_RX_MSG_TYPE: + DEBUG("conn_can_isotp_select: _TIMEOUT_MSG_VALUE\n"); + if (msg.content.value == _TIMEOUT_MSG_VALUE) { + ret = -ETIMEDOUT; + } + else { + ret = -EINTR; + } + *conn = NULL; + break; + default: + DEBUG("conn_can_isotp_select: %d\n", msg.type); + *conn = NULL; + ret = -EINTR; + break; + } + + return ret; +} +#endif /* MODULE_CONN_CAN_ISOTP_MULTI */ + +#else +typedef int dont_be_pedantic; +#endif /* MODULE_CAN_ISOTP */ diff --git a/sys/can/conn/raw.c b/sys/can/conn/raw.c new file mode 100644 index 000000000..6d3678ddc --- /dev/null +++ b/sys/can/conn/raw.c @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @file + * @brief Implementation of raw CAN connection + * + * @author Vincent Dupont + */ + +#include +#include + +#include "can/conn/raw.h" +#include "can/can.h" +#include "can/raw.h" +#include "timex.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include "xtimer.h" + +#define _TIMEOUT_TX_MSG_TYPE (0x8000) +#define _TIMEOUT_RX_MSG_TYPE (0x8001) +#define _CLOSE_CONN_MSG_TYPE (0x8002) +#define _TIMEOUT_MSG_VALUE (0xABCDEFAB) + +#ifndef CONN_CAN_RAW_TIMEOUT_TX_CONF +#define CONN_CAN_RAW_TIMEOUT_TX_CONF (1 * US_PER_SEC) +#endif + +int conn_can_raw_create(conn_can_raw_t *conn, struct can_filter *filter, size_t count, + int ifnum, int flags) +{ + assert(conn != NULL); + assert(ifnum < CAN_DLL_NUMOF); + + DEBUG("conn_can_raw_create: create conn=%p, ifnum=%d flags=%d\n", (void *)conn, ifnum, flags); + + mbox_init(&conn->mbox, conn->mbox_queue, CONN_CAN_RAW_MBOX_SIZE); + conn->flags = flags; + conn->count = 0; + conn->ifnum = ifnum; + + if (flags & CONN_CAN_RECVONLY) { + can_opt_t opt; + opt.opt = CANOPT_STATE; + canopt_state_t state = CANOPT_STATE_LISTEN_ONLY; + opt.data = &state; + opt.data_len = sizeof(state); + int ret = raw_can_set_can_opt(ifnum, &opt); + if (ret < 0) { + return ret; + } + } + + return conn_can_raw_set_filter(conn, filter, count); +} + + +int conn_can_raw_set_filter(conn_can_raw_t *conn, struct can_filter *filter, size_t count) +{ + assert(conn != NULL); + assert(filter != NULL || count == 0); + + DEBUG("conn_can_raw_set_filter: conn=%p, filter=%p, count=%d\n", + (void *)conn, (void *)filter, count); + DEBUG("conn_can_raw_set_filter: conn->filter=%p, conn->count=%d\n", + (void *)conn->filter, conn->count); + + /* unset previous filters */ + if (conn->count) { + for (size_t i = 0; i < conn->count; i++) { + DEBUG("conn_can_raw_set_filter: unsetting filter=0x%" PRIx32 ", mask=0x%" PRIx32 "\n", + conn->filter[i].can_id, conn->filter[i].can_mask); + raw_can_unsubscribe_rx_mbox(conn->ifnum, &conn->filter[i], &conn->mbox, conn); + } + } + + for (size_t i = 0; i < count; i++) { + DEBUG("conn_can_raw_set_filter: setting filter=0x%" PRIx32 ", mask=0x%" PRIx32 "\n", + filter[i].can_id, filter[i].can_mask); + int ret = raw_can_subscribe_rx_mbox(conn->ifnum, &filter[i], &conn->mbox, conn); + if (ret < 0) { + DEBUG("conn_can_raw_set_filter: error setting filters %d\n", ret); + for (size_t j = 0; j < i; j++) { + DEBUG("conn_can_raw_set_filter: unsetting filter=0x%" PRIx32 ", mask=0x%" PRIx32 "\n", + filter[j].can_id, filter[j].can_mask); + raw_can_unsubscribe_rx_mbox(conn->ifnum, &filter[j], &conn->mbox, conn); + } + return ret; + } + } + + conn->filter = filter; + conn->count = count; + + return 0; +} + +static void _tx_conf_timeout(void *arg) +{ + conn_can_raw_t *conn = arg; + msg_t msg; + + msg.type = _TIMEOUT_TX_MSG_TYPE; + msg.content.value = _TIMEOUT_MSG_VALUE; + + mbox_put(&conn->mbox, &msg); +} + +int conn_can_raw_send(conn_can_raw_t *conn, const struct can_frame *frame, int flags) +{ + assert(conn != NULL); + assert(conn->ifnum < CAN_DLL_NUMOF); + assert((conn->flags & CONN_CAN_RECVONLY) == 0); + assert(frame != NULL); + + int ret = 0; + int handle; + + DEBUG("conn_can_raw_send: conn=%p, frame=%p, flags=%d\n", + (void *)conn, (void *)frame, flags); + + if (flags & CONN_CAN_DONTWAIT) { + handle = ret = raw_can_send(conn->ifnum, frame, 0); + if (ret >= 0) { + ret = 0; + } + } + else { + xtimer_t timer; + timer.callback = _tx_conf_timeout; + timer.arg = conn; + xtimer_set(&timer, CONN_CAN_RAW_TIMEOUT_TX_CONF); + + handle = raw_can_send_mbox(conn->ifnum, frame, &conn->mbox); + if (handle < 0) { + xtimer_remove(&timer); + return handle; + } + + msg_t msg; + int timeout = 5; + while (1) { + mbox_get(&conn->mbox, &msg); + switch (msg.type) { + case CAN_MSG_TX_ERROR: + xtimer_remove(&timer); + return -EIO; + case CAN_MSG_TX_CONFIRMATION: + xtimer_remove(&timer); + if ((int)msg.content.value == handle) { + DEBUG("conn_can_raw_send: frame sent correctly\n"); + return 0; + } + else { + raw_can_abort(conn->ifnum, handle); + return -EINTR; + } + break; + case _TIMEOUT_TX_MSG_TYPE: + DEBUG("conn_can_raw_send: timeout\n"); + return -ETIMEDOUT; + break; + default: + DEBUG("conn_can_raw_send: unexpected msg=%x, requeing\n", msg.type); + mbox_put(&conn->mbox, &msg); + if (!timeout--) { + return -EINTR; + } + break; + } + } + } + + return ret; +} + +static void _rx_timeout(void *arg) +{ + conn_can_raw_t *conn = arg; + msg_t msg; + + msg.type = _TIMEOUT_RX_MSG_TYPE; + msg.content.value = _TIMEOUT_MSG_VALUE; + + mbox_put(&conn->mbox, &msg); +} + +int conn_can_raw_recv(conn_can_raw_t *conn, struct can_frame *frame, uint32_t timeout) +{ + assert(conn != NULL); + assert(conn->ifnum < CAN_DLL_NUMOF); + assert(frame != NULL); + + xtimer_t timer; + + if (timeout != 0) { + timer.callback = _rx_timeout; + timer.arg = conn; + xtimer_set(&timer, timeout); + } + + int ret; + msg_t msg; + can_rx_data_t *rx; + + mbox_get(&conn->mbox, &msg); + if (timeout != 0) { + xtimer_remove(&timer); + } + switch (msg.type) { + case CAN_MSG_RX_INDICATION: + DEBUG("conn_can_raw_recv: CAN_MSG_RX_INDICATION\n"); + rx = msg.content.ptr; + memcpy(frame, rx->data.iov_base, rx->data.iov_len); + ret = rx->data.iov_len; + raw_can_free_frame(rx); + break; + case _TIMEOUT_RX_MSG_TYPE: + if (msg.content.value == _TIMEOUT_MSG_VALUE) { + ret = -ETIMEDOUT; + } + else { + ret = -EINTR; + } + break; + case _CLOSE_CONN_MSG_TYPE: + if (msg.content.ptr == conn) { + ret = -ECONNABORTED; + } + else { + ret = -EINTR; + } + break; + default: + mbox_put(&conn->mbox, &msg); + ret = -EINTR; + break; + } + + return ret; +} + +int conn_can_raw_close(conn_can_raw_t *conn) +{ + assert(conn != NULL); + assert(conn->ifnum < CAN_DLL_NUMOF); + + DEBUG("conn_can_raw_close: conn=%p\n", (void *)conn); + + if (conn->count) { + for (size_t i = 0; i < conn->count; i++) { + DEBUG("conn_can_raw_close: unsetting filter=0x%" PRIx32 ", mask=0x%" PRIx32 "\n", + conn->filter[i].can_id, conn->filter[i].can_mask); + raw_can_unsubscribe_rx_mbox(conn->ifnum, &conn->filter[i], &conn->mbox, conn); + } + conn->count = 0; + msg_t msg; + while (mbox_try_get(&conn->mbox, &msg)) { + if (msg.type == CAN_MSG_RX_INDICATION) { + DEBUG("conn_can_raw_close: incoming msg pending, freeing\n"); + raw_can_free_frame(msg.content.ptr); + } + } + msg.type = _CLOSE_CONN_MSG_TYPE; + msg.content.ptr = conn; + mbox_try_put(&conn->mbox, &msg); + } + + return 0; +} diff --git a/sys/include/can/conn/isotp.h b/sys/include/can/conn/isotp.h new file mode 100644 index 000000000..e0139aa3f --- /dev/null +++ b/sys/include/can/conn/isotp.h @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup conn_can + * @{ + * + * + * @file + * @brief Definitions of generic CAN interface + * + * @author Vincent Dupont + * + */ + +#ifndef CAN_CONN_ISOTP_H +#define CAN_CONN_ISOTP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "can/can.h" +#include "can/isotp.h" +#include "mbox.h" + +#if defined(MODULE_CONN_CAN_ISOTP_MULTI) || defined(DOXYGEN) +#include "mutex.h" + +#ifndef CONN_CAN_ISOTP_MBOX_SIZE +/** + * @brief Mailbox size of a conn_can_isotp_t + */ +#define CONN_CAN_ISOTP_MBOX_SIZE (16) +#endif + +/** + * @brief ISO-TP connection + * + * When conn_can_isotp_multi module is used, this is a 'master' connection + * which can be used to send and receive with multiple connections within + * a single thread. + * + * If conn_can_isotp_multi is not used, this is a simple ISO-TP connection + */ +typedef struct conn_can_isotp_master conn_can_isotp_t; + +/** + * @brief ISO-TP salve connection + * + * This is a slave connection which exists only when conn_can_isotp_multi + * module is used. + */ +typedef struct conn_can_isotp_slave { + struct conn_can_isotp_slave *next; /**< Next slave in the list */ + struct conn_can_isotp_master *master; /**< Master connection holding the mailbox */ + struct isotp isotp; /**< ISO-TP parameters and status */ + int ifnum; /**< interface number */ + int bound; /**< 1 if connection is bound */ + can_rx_data_t *rx; /**< Buffered rx data */ +} conn_can_isotp_slave_t; + +/** + * @brief ISO-TP master connection + */ +struct conn_can_isotp_master { + /* slave fields */ + struct conn_can_isotp_slave *next; /**< First slave in the list */ + struct conn_can_isotp_master *master; /**< Master connection */ + struct isotp isotp; /**< ISO-TP parameters and status */ + int ifnum; /**< interface number */ + int bound; /**< 1 if connection is bound */ + can_rx_data_t *rx; /**< Buffered rx data */ + /* slave fields end */ + mutex_t lock; /**< Master lock */ + mbox_t mbox; /**< mailbox for the connection list */ + /** Connection list message queue */ + msg_t mbox_queue[CONN_CAN_ISOTP_MBOX_SIZE]; +}; + +/** + * @brief Initialize a slave connection + * + * This initializes a slave connection. + * + * This must be called on slave connections when conn_can_isotp_multi is used. + * Does not exist otherwise. + * + * @param[in] master the master connection + * @param[inout] slave the slave connection to initialize + */ +static inline void conn_can_isotp_init_slave(conn_can_isotp_t *master, conn_can_isotp_slave_t *slave) +{ + slave->next = NULL; + slave->master = master; + slave->rx = NULL; +} +#else + +#ifndef CONN_CAN_ISOTP_MBOX_SIZE +/** + * @brief Mailbox size of a conn_can_isotp_t + */ +#define CONN_CAN_ISOTP_MBOX_SIZE (16) +#endif + +/** + * @brief ISOTP connection + */ +typedef struct conn_can_isotp { + struct isotp isotp; /**< ISO-TP connection */ + int ifnum; /**< interface number */ + int bound; /**< 1 if connection is bound */ + mbox_t mbox; /**< mbox */ + /** message queue */ + msg_t mbox_queue[CONN_CAN_ISOTP_MBOX_SIZE]; +} conn_can_isotp_t; +#endif /* MODULE_CONN_CAN_ISOTP_MULTI */ + +/** + * @brief Create can isotp connection socket + * + * @param[inout] conn ISO-TP connection + * @param[in] options ISO-TP options + * @param[in] ifnum can device Interface + * + * @return 0 if socket was successfully connected + * @return any other negative number in case of an error + */ +int conn_can_isotp_create(conn_can_isotp_t *conn, struct isotp_options *options, int ifnum); + +/** + * @brief Bind a can isotp connection + * + * @param[inout] conn ISO-TP connection + * + * @return 0 on success + * @return any other negative number in case of an error + */ +int conn_can_isotp_bind(conn_can_isotp_t *conn); + +/** + * @brief Close can isotp connection socket + * + * @param[in] conn ISO-TP connection + * + * @return 0 if conn is closed correctly + * @return any other negative number in case of an error + */ +int conn_can_isotp_close(conn_can_isotp_t *conn); + +/** + * @brief Receive isotp data + * + * @param[in] conn ISO-TP connection + * @param[out] buf buf to fill in with received data + * @param[in] size size of the buffer in bytes + * @param[in] timeout timeout in us, 0 for infinite + * + * @return the number of bytes received + * @return any other negative number in case of an error + */ +int conn_can_isotp_recv(conn_can_isotp_t *conn, void *buf, size_t size, uint32_t timeout); + +/** + * @brief Generic can send + * + * @param[in] conn ISO-TP connection + * @param[in] buf data to send + * @param[in] size size of the buffer in bytes + * @param[in] flags make function blocked or not + * (CAN_ISOTP_TX_DONT_WAIT to ignore tx confirmation) + * + * @return the number of bytes sent + * @return any other negative number in case of an error + */ +int conn_can_isotp_send(conn_can_isotp_t *conn, const void *buf, size_t size, int flags); + +#if defined(MODULE_CONN_CAN_ISOTP_MULTI) || defined(DOXYGEN) +/** + * @brief Wait for reception from multiple connections + * + * @param[out] conn ISO-TP connection which received data + * @param[in] master the master connection + * @param[in] timeout timeout in us, 0 for infinite wait + * + * @return 0 if OK, < 0 if error + */ +int conn_can_isotp_select(conn_can_isotp_slave_t **conn, conn_can_isotp_t *master, uint32_t timeout); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CAN_CONN_ISOTP_H */ +/** @} */ diff --git a/sys/include/can/conn/raw.h b/sys/include/can/conn/raw.h new file mode 100644 index 000000000..fee1fbc06 --- /dev/null +++ b/sys/include/can/conn/raw.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup can + * @defgroup conn_can Connection + * @brief conn interface for CAN stack + * + * This is the user interface to send and receive raw CAN frames or ISO-TP datagrams + * + * @{ + * + * + * @file + * @brief Definitions of generic CAN interface + * + * @author Vincent Dupont + * + */ + +#ifndef CAN_CONN_RAW_H +#define CAN_CONN_RAW_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "can/can.h" +#include "can/raw.h" +#include "mbox.h" + +#ifndef CONN_CAN_RAW_MBOX_SIZE +/** + * @brief Mailbox size of a conn_can_raw_t + */ +#define CONN_CAN_RAW_MBOX_SIZE (16) +#endif + +/** + * @name flags values + * @{ + */ +#define CONN_CAN_DONTWAIT (1) /**< Do not wait for Tx confirmation when sending */ +#define CONN_CAN_RECVONLY (2) /**< Do not send anything on the bus */ +/** @} */ + +/** + * @brief RAW CAN connection + */ +typedef struct conn_can_raw { + int ifnum; /**< Interface number of the can device */ + int flags; /**< Config flags for that conn object */ + size_t count; /**< number of filters set */ + struct can_filter *filter; /**< list of filter */ + mbox_t mbox; /**< mbox */ + /** + * message queue + */ + msg_t mbox_queue[CONN_CAN_RAW_MBOX_SIZE]; +} conn_can_raw_t; + +/** + * @brief Create can connection socket + * + * @param[inout] conn CAN connection + * @param[in] filter list of filters to set + * @param[in] count number of filters in @p filter + * @param[in] ifnum can device Interface + * @param[in] flags conn flags to set (CONN_CAN_RECVONLY) + * + * @return 0 if socket was successfully connected + * @return any other negative number in case of an error + */ +int conn_can_raw_create(conn_can_raw_t *conn, struct can_filter *filter, size_t count, + int ifnum, int flags); + +/** + * @brief Close can connection socket + * + * @param[in] conn CAN connection + * + * @return 0 if conn is closed correctly + * @return any other negative number in case of an error. + */ +int conn_can_raw_close(conn_can_raw_t *conn); + +/** + * @brief Generic can receive + * + * @param[in] conn CAN connection + * @param[out] frame CAN frame to receive + * @param[in] timeout timeout in us, 0 for infinite + * + * @return the number of bytes received + * @return any other negative number in case of an error + */ +int conn_can_raw_recv(conn_can_raw_t *conn, struct can_frame *frame, uint32_t timeout); + +/** + * @brief Generic can send + * + * @param[in] conn CAN connection + * @param[in] frame frame to send + * @param[in] flags make function blocked or not + * (CONN_CAN_DONTWAIT to ignore tx confirmation) + * + * @return the number of bytes sent + * @return any other negative number in case of an error + */ +int conn_can_raw_send(conn_can_raw_t *conn, const struct can_frame *frame, int flags); + +/** + * @brief Set raw CAN filters + * + * If filters were already set for this connection, it first unsets the previous filters + * and sets the new ones. + * + * @param[in] conn CAN connection + * @param[in] filter list of filters to set + * @param[in] count number of filters in @p filter + * + * @return 0 if can filters were successfully set + * @return any other negative number in case of an error + */ +int conn_can_raw_set_filter(conn_can_raw_t *conn, struct can_filter *filter, size_t count); + + +#ifdef __cplusplus +} +#endif + +#endif /* CAN_CONN_RAW_H */ +/** @} */ From 0a052f6b976e01e1c8ca72396bc8d4e60d7675bb Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Fri, 19 May 2017 15:39:07 +0200 Subject: [PATCH 6/9] can: add conn_can test app --- tests/conn_can/Makefile | 23 ++ tests/conn_can/README.md | 152 ++++++++++ tests/conn_can/main.c | 634 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 809 insertions(+) create mode 100644 tests/conn_can/Makefile create mode 100644 tests/conn_can/README.md create mode 100644 tests/conn_can/main.c diff --git a/tests/conn_can/Makefile b/tests/conn_can/Makefile new file mode 100644 index 000000000..3de0ceee9 --- /dev/null +++ b/tests/conn_can/Makefile @@ -0,0 +1,23 @@ +export APPLICATION = can +include ../Makefile.tests_common + +BOARD_INSUFFICIENT_MEMORY := chronos msb-430 msb-430h nucleo32-f031 nucleo32-f042 \ + nucleo32-f303 nucleo32-l031 nucleo-f030 nucleo-f070 \ + nucleo-f072 nucleo-f302 nucleo-f303 nucleo-f334 \ + nucleo-l053 stm32f0discovery telosb weio wsn430-v1_3b \ + wsn430-v1_4 z1 + + +CFLAGS += -DDEVELHELP +CFLAGS += -DLOG_LEVEL=LOG_ALL + +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps + +USEMODULE += conn_can +USEMODULE += can_isotp +USEMODULE += conn_can_isotp_multi +USEMODULE += can_pm + +include $(RIOTBASE)/Makefile.include diff --git a/tests/conn_can/README.md b/tests/conn_can/README.md new file mode 100644 index 000000000..3dca75054 --- /dev/null +++ b/tests/conn_can/README.md @@ -0,0 +1,152 @@ +tests/conn_can +================ +Demo application for the CAN stack with conn_can interface. + + +Native prerequisites +============ +For using the can stack on top of socketCAN, available for linux, you need: +- socketCAN (part of kernel starting from 2.6.25) +- install the 32bit version of libsocketcan: + +if you're on a 64bit system: +``` +sudo dpkg --add-architecture i386 +sudo apt-get update +sudo apt-get install libsocketcan-dev:i386 +``` +On 32 bit you can just do the following: +``` +sudo apt-get install libsocketcan-dev +``` + +Alternatively, you can compile from source: +``` +wget http://www.pengutronix.de/software/libsocketcan/download/libsocketcan-0.0.10.tar.bz2 + +$ sudo tar xvjf libsocketcan-0.0.10.tar.bz2 + +$ sudo rm -rf libsocketcan-0.0.10.tar.bz2 + +$ sudo cd libsocketcan-0.0.10 + +$ sudo ./configure + +compile in 32bits + +./configure --build=i686-pc-linux-gnu "CFLAGS=-m32" "CXXFLAG + +$ sudo make + +$ sudo make install + + +sudo ldconfig +/usr/local/lib +``` + +The default native configuration defines two virtual can ifaces to be used. +Before running this test on native, you should create those: + +``` +sudo modprobe vcan +sudo ip link add dev vcan0 type vcan +sudo ip link add dev vcan1 type vcan +sudo ip link set vcan0 up +sudo ip link set vcan1 up +``` + +Usage +===== + +Build, flash and start the application: +``` +export BOARD=your_board +make +make flash +make term +``` + +The CAN interfaces are registered at startup to the dll. The list of registered +interfaces and their RIOT names can be retrieved with: +``` +can list +``` + +To send a raw CAN frame, id 0x100 with 2 bytes of data 01 02 on interface 0: +``` +can send 0 100 01 02 +``` + +Two threads are launched to enable receiving frames. To receive raw CAN frames, +ids 0x100 and 0x500 with thread 0 on interface 1, with 10s timeout: +``` +can recv 1 0 10000000 100 500 +``` + +A connection can be closed with its thread id, for instance: +``` +can close 0 +``` + + +To send an ISO-TP datagram, first bind a connection with one of the threads, +source id 700, dest id 708, thread 1 and interface 0: +``` +can bind_isotp 0 1 700 708 +``` +Then send the data 01 02 03 04 05 0a 0b 0c: +``` +can send_isotp 1 01 02 03 04 05 0a 0b 0c +``` + +To receive from an ISO-TP channel, it must be bound, then with the previous channel, +and 10s timeout: +``` +can recv_isotp 1 10000000 +``` + +An ISO-TP channel can be closed with: +``` +can close_isotp 1 +``` + +You can also set a bitrate (this won't work on native with vcan, only with real +interfaces, but then root access are needed), for instance 250000 bit/s with +sampling point 87.5%: +``` +can set_bitrate 250000 875 +``` + +Linux CAN basic commands +======================== + +Once the interfaces are set up, can-utils commands provide a way to send and receive +raw CAN frames and ISO-TP datagrams. + +For ISO-TP, an experimental module for linux can be found [here](https://github.com/hartkopp/can-isotp). +It needs to be loaded before trying to use ISO-TP protocol. + +Here are some basics examples. + +Send a raw CAN frame, id 0x100, data 00 11 22: +``` +cansend vcan0 100#001122 +``` + +Dump the traffic on a CAN interface: +``` +candump vcan0 +``` + +Send an ISO-TP datagram, source id 700, dest id 708, data 00 11 22 33 aa bb cc dd: +``` +echo 00 11 22 33 aa bb cc dd | isotpsend -s 700 -d 708 vcan0 +``` + +Receive ISO-TP datagram: +``` +isotprecv -s 708 -d 700 vcan0 +``` + +Please read commands help for more details on usage. diff --git a/tests/conn_can/main.c b/tests/conn_can/main.c new file mode 100644 index 000000000..0bce87e1b --- /dev/null +++ b/tests/conn_can/main.c @@ -0,0 +1,634 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief main + * + * @author Vincent Dupont + * + * @} + */ +#include +#include +#include +#include + +#include "shell.h" +#include "board.h" +#include "periph/gpio.h" +#include "thread.h" + +#include "can/can.h" +#include "can/conn/raw.h" +#include "can/conn/isotp.h" +#include "can/device.h" + +#define THREAD_STACKSIZE (THREAD_STACKSIZE_MAIN) +#define RECEIVE_THREAD_MSG_QUEUE_SIZE (8) + +#include "timex.h" +#define TEST_CONN_CAN_RECV_TIMEOUT (30 * US_PER_SEC) + +#define RCV_THREAD_NUMOF (2) + +#define MAX_FILTER (16) + +#define CAN_MSG_RECV 0x400 +#define CAN_MSG_BIND_ISOTP 0x401 +#define CAN_MSG_RECV_ISOTP 0x402 +#define CAN_MSG_CLOSE_ISOTP 0x403 +#define CAN_MSG_SEND_ISOTP 0x404 + +static char thread_stack[RCV_THREAD_NUMOF][THREAD_STACKSIZE]; +static kernel_pid_t receive_pid[RCV_THREAD_NUMOF]; + +static conn_can_raw_t conn[RCV_THREAD_NUMOF]; +static struct can_filter filters[RCV_THREAD_NUMOF][MAX_FILTER]; + +#ifdef MODULE_CAN_ISOTP +#define ISOTP_BUF_SIZE 1024 +static uint8_t isotp_buf[RCV_THREAD_NUMOF][ISOTP_BUF_SIZE]; + +static conn_can_isotp_t conn_isotp[RCV_THREAD_NUMOF]; +#endif + +static int thread_busy[RCV_THREAD_NUMOF]; + +static void print_usage(void) +{ + puts("test_can list"); + puts("test_can send ifnum can_id [B1 [B2 [B3 [B4 [B5 [B6 [B7 [B8]]]]]]]]"); + printf("test_can recv ifnum user_id timeout can_id1 [can_id2..can_id%d]\n", MAX_FILTER); + puts("test_can close user_id"); +#ifdef MODULE_CAN_ISOTP + puts("test_can bind_isotp ifnum user_id source_id dest_id"); + puts("test_can send_isotp user_id [B1 [.. [ Bn ]]]"); + puts("test_can recv_isotp user_id timeout"); + puts("test_can close_isotp user_id"); +#endif + puts("test_can get_filter ifnum"); + puts("test_can set_bitrate ifnum bitrate [sample_point]"); + puts("test_can get_bitrate ifnum"); + puts("test_can get_counter ifnum"); + puts("test_can power_up ifnum"); + puts("test_can power_down ifnum"); +} + +static int _list(int argc, char **argv) { + + (void)argc; + (void)argv; + + for (int i = 0; i < CAN_DLL_NUMOF; i++) { + const char *name = raw_can_get_name_by_ifnum(i); + if (name) { + printf("CAN #%d: %s\n", i, name); + } + else { + break; + } + } + + return 0; +} + +static int _send(int argc, char **argv) +{ + if (argc < 5) { + print_usage(); + return 1; + } + struct can_frame frame; + int ifnum = strtol(argv[2], NULL, 0); + if (ifnum >= CAN_DLL_NUMOF) { + puts("Invalid interface number"); + return 1; + } + + frame.can_id = strtoul(argv[3], NULL, 16); + frame.can_dlc = argc - 4; + if (frame.can_dlc > 8) { + puts("Invalid length"); + return 1; + } + for (int i = 0; i < frame.can_dlc; i++) { + frame.data[i] = strtol(argv[4 + i], NULL, 16); + } + conn_can_raw_t conn; + conn_can_raw_create(&conn, NULL, 0, ifnum, 0); + int ret = conn_can_raw_send(&conn, &frame, 0); + if (ret < 0) { + puts("Error when trying to send"); + } + return 0; +} + +static int _receive(int argc, char **argv) +{ + if (argc < 4) { + print_usage(); + return 1; + } + int res; + int ifnum = strtol(argv[2], NULL, 0); + if (ifnum >= CAN_DLL_NUMOF) { + puts("Invalid interface number"); + return 1; + } + + int thread_nb = strtol(argv[3], NULL, 0); + int filt_num = argc - 5; + if (thread_nb >= RCV_THREAD_NUMOF) { + printf("Invalid thread number, range=0..%d\n", RCV_THREAD_NUMOF - 1); + return 1; + } + if (thread_busy[thread_nb]) { + puts("Thread already in use"); + return 1; + } + if (filt_num > MAX_FILTER) { + puts("Too many filters"); + return 1; + } + for (int i = 0; i < filt_num; i++) { + filters[thread_nb][i].can_id = strtoul(argv[5 + i], NULL, 16); + filters[thread_nb][i].can_mask = 0xffffffff; + } + uint32_t timeout = strtoul(argv[4], NULL, 0); + msg_t msg; + msg.type = CAN_MSG_RECV; + msg.content.value = timeout; + res = conn_can_raw_create(&conn[thread_nb], filters[thread_nb], + filt_num, ifnum, 0); + if (res < 0) { + puts("Error when setting filters"); + return 1; + } + thread_busy[thread_nb] = 1; + msg_send(&msg, receive_pid[thread_nb]); + return 0; +} + +static int _close(int argc, char **argv) +{ + if (argc < 2) { + print_usage(); + return 1; + } + int thread_nb = strtol(argv[2], NULL, 0); + if (thread_nb >= RCV_THREAD_NUMOF) { + printf("Invalid thread number, range=0..%d\n", RCV_THREAD_NUMOF - 1); + return 1; + } + conn_can_raw_close(&conn[thread_nb]); + thread_busy[thread_nb] = 0; + return 0; +} + +#ifdef MODULE_CAN_ISOTP +static int _bind_isotp(int argc, char **argv) +{ + if (argc < 4) { + print_usage(); + return 1; + } + int ret; + int ifnum = strtol(argv[2], NULL, 0); + if (ifnum >= CAN_DLL_NUMOF) { + puts("Invalid interface number"); + return 1; + } + + int thread_nb = strtol(argv[3], NULL, 0); + if (thread_nb >= RCV_THREAD_NUMOF) { + printf("Invalid thread number, range=0..%d\n", RCV_THREAD_NUMOF - 1); + return 1; + } + if (thread_busy[thread_nb]) { + puts("Thread already in use"); + return 1; + } + struct isotp_options isotp_opt; + memset(&isotp_opt, 0, sizeof(isotp_opt)); + isotp_opt.tx_id = strtoul(argv[4], NULL, 16); + isotp_opt.rx_id = strtoul(argv[5], NULL, 16); + +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + conn_can_isotp_init_slave(&conn_isotp[thread_nb], (conn_can_isotp_slave_t *)&conn_isotp[thread_nb]); +#endif + ret = conn_can_isotp_create(&conn_isotp[thread_nb], &isotp_opt, ifnum); + if (ret == 0) { + ret = conn_can_isotp_bind(&conn_isotp[thread_nb]); + } + if (ret < 0) { + puts("Error when binding connection"); + return 1; + } + thread_busy[thread_nb] = 1; + return 0; +} + +static int _send_isotp(int argc, char **argv) +{ + if (argc < 4) { + print_usage(); + return 1; + } + int thread_nb = strtoul(argv[2], NULL, 0); + if (thread_nb >= RCV_THREAD_NUMOF) { + printf("Invalid thread number, range=0..%d\n", RCV_THREAD_NUMOF - 1); + return 1; + } + int len = argc - 3; + uint8_t data[len]; + + for (int i = 0; i < len; i++) { + data[i] = strtol(argv[3 + i], NULL, 16); + } + + msg_t msg, reply; + can_opt_t opt; + opt.data = data; + opt.data_len = len; + msg.type = CAN_MSG_SEND_ISOTP; + msg.content.ptr = &opt; + int res = msg_send_receive(&msg, &reply, receive_pid[thread_nb]); + if (res < 0 || (int)reply.content.value < 0) { + puts("Error when sending"); + return 1; + } + return 0; +} + +static int _receive_isotp(int argc, char **argv) +{ + if (argc < 4) { + print_usage(); + return 1; + } + int thread_nb = strtol(argv[2], NULL, 0); + if (thread_nb >= RCV_THREAD_NUMOF) { + printf("Invalid thread number, range=0..%d\n", RCV_THREAD_NUMOF - 1); + return 1; + } + uint32_t timeout = strtoul(argv[3], NULL, 0); + + msg_t msg; + msg.type = CAN_MSG_RECV_ISOTP; + msg.content.value = timeout; + msg_send(&msg, receive_pid[thread_nb]); + + return 0; +} + +static int _close_isotp(int argc, char **argv) +{ + if (argc < 2) { + print_usage(); + return 1; + } + int thread_nb = strtol(argv[2], NULL, 0); + if (thread_nb >= RCV_THREAD_NUMOF) { + printf("Invalid thread number, range=0..%d\n", RCV_THREAD_NUMOF - 1); + return 1; + } + conn_can_isotp_close(&conn_isotp[thread_nb]); + thread_busy[thread_nb] = 0; + return 0; +} +#endif /* MODULE_CAN_ISOTP */ + +static int _get_filter(int argc, char **argv) +{ + if (argc < 3) { + print_usage(); + return 1; + } + int res; + int ifnum = strtol(argv[2], NULL, 0); + struct can_filter filters[32]; + can_opt_t opt; + opt.data = (void *)filters; + opt.data_len = sizeof(filters); + opt.opt = CANOPT_RX_FILTERS; + res = raw_can_get_can_opt(ifnum, &opt); + if (res < 0) { + puts("Error when reading filters"); + } + else if (res == 0) { + puts("No filter set"); + } + else { + for (unsigned int i = 0; i < res / sizeof(filters[0]); i++) { + printf("Filter %d: 0x%" PRIx32"\n", i, filters[i].can_id); + printf("Mask %d: 0x%" PRIx32"\n", i, filters[i].can_mask); + } + } + return 0; +} + +static int _set_bitrate(int argc, char **argv) +{ + if (argc < 4) { + print_usage(); + return 1; + } + + int ifnum = strtol(argv[2], NULL, 0); + if (ifnum >= CAN_DLL_NUMOF) { + printf("Invalid ifnum %d\n", ifnum); + return 1; + } + uint32_t bitrate = strtoul(argv[3], NULL, 0); + uint32_t sample_point = 0; + + int ret; + if (argc > 4) { + sample_point = strtoul(argv[4], NULL, 0); + } + printf("Setting bitrate=%" PRIu32 ", sample point=%" PRIu32 "\n", + bitrate, sample_point); + ret = raw_can_set_bitrate(ifnum, bitrate, sample_point); + if (ret < 0) { + printf("Error when setting bitrate: res=%d\n", ret); + return 1; + } + else if (ret == 1) { + puts("Bitrate/sample_point cannot be reached"); + } + + puts("Bittimings successfully set"); + + return 0; +} + +static int _get_bitrate(int argc, char **argv) +{ + if (argc < 3) { + print_usage(); + return 1; + } + + int ifnum = strtol(argv[2], NULL, 0); + struct can_bittiming bittiming; + can_opt_t opt; + opt.data = &bittiming; + opt.data_len = sizeof(bittiming); + opt.opt = CANOPT_BITTIMING; + + int ret = raw_can_get_can_opt(ifnum, &opt); + if (ret < 0) { + printf("Error when getting bitrate: res=%d\n", ret); + return 1; + } + + printf("Bitrate read: bitrate=%" PRIu32 ", sample_point=%" PRIu32 + "\nbrp=%" PRIu32 "phase-seg1=%" PRIu32 + ", phase-seg2=%" PRIu32 ", sjw=%" PRIu32 "\n", bittiming.bitrate, + bittiming.sample_point, bittiming.brp, bittiming.phase_seg1, + bittiming.phase_seg2, bittiming.sjw); + return 0; +} + +static int _get_counter(int argc, char **argv) +{ + if (argc < 3) { + print_usage(); + return 1; + } + + int res = 0; + int ifnum = strtol(argv[2], NULL, 0); + if (ifnum >= CAN_DLL_NUMOF) { + puts("Invalid interface number"); + return 1; + } + + uint16_t cnt; + can_opt_t opt; + opt.data = &cnt; + opt.data_len = sizeof(cnt); + opt.opt = CANOPT_TEC; + + int ret = raw_can_get_can_opt(ifnum, &opt); + if (ret < 0) { + printf("Error when getting TEC: res=%d\n", ret); + res = 1; + } + else { + printf("TEC=%" PRIu16, cnt); + } + + opt.opt = CANOPT_REC; + + ret = raw_can_get_can_opt(ifnum, &opt); + if (ret < 0) { + printf("\nError when getting REC: res=%d\n", ret); + res = 1; + } + else { + printf(", REC=%" PRIu16 "\n", cnt); + } + + return res; +} + +static int _power_up(int argc, char **argv) +{ + if (argc < 3) { + print_usage(); + return 1; + } + + int res = 0; + int ifnum = strtol(argv[2], NULL, 0); + if (ifnum >= CAN_DLL_NUMOF) { + puts("Invalid interface number"); + return 1; + } + + int ret = raw_can_power_up(ifnum); + if (ret < 0) { + printf("Error when powering up: res=%d\n", ret); + res = 1; + } + + return res; +} + +static int _power_down(int argc, char **argv) +{ + if (argc < 3) { + print_usage(); + return 1; + } + + int res = 0; + int ifnum = strtol(argv[2], NULL, 0); + if (ifnum >= CAN_DLL_NUMOF) { + puts("Invalid interface number"); + return 1; + } + + int ret = raw_can_power_down(ifnum); + if (ret < 0) { + printf("Error when powering up: res=%d\n", ret); + res = 1; + } + + return res; +} + +static int _can_handler(int argc, char **argv) +{ + if (argc < 2) { + print_usage(); + return 1; + } + else if (strncmp(argv[1], "list", 5) == 0) { + return _list(argc, argv); + } + else if (strncmp(argv[1], "send", 5) == 0) { + return _send(argc, argv); + } + else if (strncmp(argv[1], "recv", 5) == 0) { + return _receive(argc, argv); + } + else if (strncmp(argv[1], "close", 6) == 0) { + return _close(argc, argv); + } +#ifdef MODULE_CAN_ISOTP + else if (strncmp(argv[1], "bind_isotp", 11) == 0) { + return _bind_isotp(argc, argv); + } + else if (strncmp(argv[1], "send_isotp", 11) == 0) { + return _send_isotp(argc, argv); + } + else if (strncmp(argv[1], "recv_isotp", 11) == 0) { + return _receive_isotp(argc, argv); + } + else if (strncmp(argv[1], "close_isotp", 12) == 0) { + return _close_isotp(argc, argv); + } +#endif + else if (strncmp(argv[1], "get_filter", 10) == 0) { + return _get_filter(argc, argv); + } + else if (strncmp(argv[1], "set_bitrate", 11) == 0) { + return _set_bitrate(argc, argv); + } + else if (strncmp(argv[1], "get_bitrate", 11) == 0) { + return _get_bitrate(argc, argv); + } + else if (strncmp(argv[1], "get_counter", 11) == 0) { + return _get_counter(argc, argv); + } + else if (strncmp(argv[1], "power_up", 9) == 0) { + return _power_up(argc, argv); + } + else if (strncmp(argv[1], "power_down", 11) == 0) { + return _power_down(argc, argv); + } + else { + printf("unknown command: %s\n", argv[1]); + return 1; + } +} + +static void *_receive_thread(void *args) +{ + int thread_nb = (int)args; + struct can_frame frame; + msg_t msg, msg_queue[RECEIVE_THREAD_MSG_QUEUE_SIZE]; + + /* setup the device layers message queue */ + msg_init_queue(msg_queue, RECEIVE_THREAD_MSG_QUEUE_SIZE); + + printf("%d: launching receive_thread\n", thread_nb); + + while (1) { + msg_receive(&msg); + switch (msg.type) { + case CAN_MSG_RECV: + { + int ret; + while ((ret = conn_can_raw_recv(&conn[thread_nb], &frame, msg.content.value)) + == sizeof(struct can_frame)) { + printf("%d: %-8s %" PRIx32 " [%x] ", + thread_nb, raw_can_get_name_by_ifnum(conn[thread_nb].ifnum), + frame.can_id, frame.can_dlc); + for (int i = 0; i < frame.can_dlc; i++) { + printf(" %02X", frame.data[i]); + } + printf("\n"); + } + printf("%d: recv terminated: ret=%d\n", thread_nb, ret); + conn_can_raw_close(&conn[thread_nb]); + thread_busy[thread_nb] = 0; + break; + } +#ifdef MODULE_CAN_ISOTP + case CAN_MSG_RECV_ISOTP: + { + int ret; + while ((ret = conn_can_isotp_recv(&conn_isotp[thread_nb], isotp_buf[thread_nb], + ISOTP_BUF_SIZE, msg.content.value)) + <= ISOTP_BUF_SIZE && ret >= 0) { + printf("%d: %-8s ISOTP [%d] ", + thread_nb, raw_can_get_name_by_ifnum(conn_isotp[thread_nb].ifnum), ret); + for (int i = 0; i < ret; i++) { + printf(" %02X", isotp_buf[thread_nb][i]); + } + printf("\n"); + } + printf("%d: recv terminated: ret=%d\n", thread_nb, ret); + break; + } + case CAN_MSG_SEND_ISOTP: + { + msg_t reply; + can_opt_t *opt = msg.content.ptr; + int ret = conn_can_isotp_send(&conn_isotp[thread_nb], opt->data, opt->data_len, 0); + reply.type = msg.type; + reply.content.value = ret; + msg_reply(&msg, &reply); + break; + } +#endif /* MODULE_CAN_ISOTP */ + default: + printf("%d: _receive_thread: received unknown message\n", thread_nb); + break; + } + } + + return NULL; +} + +static const shell_command_t _commands[] = { + {"test_can", "Test CAN functions", _can_handler}, + { NULL, NULL, NULL}, +}; + +int main(void) +{ + for (int i = 0; i < RCV_THREAD_NUMOF; i++) { + receive_pid[i] = thread_create(thread_stack[i], THREAD_STACKSIZE, + THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_STACKTEST, _receive_thread, + (void*)i, "receive_thread"); + } + + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +} From 890519913521ffda10e6408f9f0aaeed235e754d Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Fri, 19 May 2017 15:41:53 +0200 Subject: [PATCH 7/9] can_trx: add CAN transceiver interface --- drivers/can_trx/Makefile | 1 + drivers/can_trx/can_trx.c | 53 +++++++++++++++++ drivers/include/can/can_trx.h | 108 ++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 drivers/can_trx/Makefile create mode 100644 drivers/can_trx/can_trx.c create mode 100644 drivers/include/can/can_trx.h diff --git a/drivers/can_trx/Makefile b/drivers/can_trx/Makefile new file mode 100644 index 000000000..48422e909 --- /dev/null +++ b/drivers/can_trx/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/can_trx/can_trx.c b/drivers/can_trx/can_trx.c new file mode 100644 index 000000000..0f6057fc8 --- /dev/null +++ b/drivers/can_trx/can_trx.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup drivers_can transceiver + * @ingroup drivers + * @brief generic transceiver interface + * + * @{ + * + * @file + * @brief generic transceiver interface + * + * @author Vincent Dupont + */ + +#include +#include + +#include "can/can_trx.h" + +int can_trx_init(can_trx_t *dev) +{ + if (dev == NULL) { + return -ENODEV; + } + + if (dev->driver->init) { + return dev->driver->init(dev); + } + else { + return -ENOTSUP; + } +} + +int can_trx_set_mode(can_trx_t *dev, can_trx_mode_t mode) +{ + if (dev == NULL) { + return -ENODEV; + } + + if (dev->driver->set_mode) { + return dev->driver->set_mode(dev, mode); + } + else { + return -ENOTSUP; + } +} diff --git a/drivers/include/can/can_trx.h b/drivers/include/can/can_trx.h new file mode 100644 index 000000000..432b81ca0 --- /dev/null +++ b/drivers/include/can/can_trx.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup trx_can CAN transceiver + * @ingroup can + * @ingroup drivers + * @brief generic transceiver interface + * + * @{ + * + * @file + * @brief generic transceiver interface + * + * @author Aurelien Gonce + * @author Vincent Dupont + */ +#ifndef CAN_CAN_TRX_H +#define CAN_CAN_TRX_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * trx transceiver mode + */ +typedef enum { + TRX_NORMAL_MODE = 0, + TRX_SILENT_MODE, + TRX_SLEEP_MODE, + /* single wire can modes */ + TRX_HIGH_SPEED_MODE, + TRX_HIGH_VOLTAGE_WAKE_UP_MODE +} can_trx_mode_t; + +/** + * @brief forward declaration of trx_driver + */ +typedef struct trx_driver trx_driver_t; + +/** + * @brief Generic transceiver descriptor + */ +typedef struct can_trx { + const trx_driver_t *driver; /**< driver */ + can_trx_mode_t mode; /**< current mode */ +} can_trx_t; + +/** + * @brief Generic transceiver driver + */ +struct trx_driver { + /** + * @brief initialize the trx device + * + * @param[in] dev Transceiver to initialize + * + * @return 0 on success + * @return < 0 on error + */ + int (*init)(can_trx_t *dev); + + /** + * @brief set mode interface + * + * @param[in] dev Transceiver to set + * @param[in] mode Mode to set + * + * @return 0 on success + * @return < 0 on error + */ + int (*set_mode)(can_trx_t *dev, can_trx_mode_t mode); +}; + +/** + * @brief initialize a transceiver + * + * @param[in] dev Transceiver to initialize + * + * @return 0 on success + * @return < 0 on error + */ +int can_trx_init(can_trx_t *dev); + +/** + * @brief transceiver set mode + * + * @param[in] dev Transceiver to set + * @param[in] mode Mode to set + * + * @return 0 on success + * @return < 0 on error + */ +int can_trx_set_mode(can_trx_t *dev, can_trx_mode_t mode); + + +#ifdef __cplusplus +} +#endif + +#endif /* CAN_CAN_TRX_H */ +/** @} */ From 029a127eb3fe9b37c079f5443335175011150257 Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Fri, 19 May 2017 15:45:56 +0200 Subject: [PATCH 8/9] can: add transceiver support in CAN stack --- sys/auto_init/can/auto_init_can_native.c | 3 +++ sys/can/device.c | 27 ++++++++++++++++++++++++ sys/can/dll.c | 17 +++++++++++++++ sys/include/can/common.h | 3 +++ sys/include/can/device.h | 9 ++++++++ sys/include/can/raw.h | 18 ++++++++++++++++ tests/conn_can/Makefile | 1 + 7 files changed, 78 insertions(+) diff --git a/sys/auto_init/can/auto_init_can_native.c b/sys/auto_init/can/auto_init_can_native.c index 7b0c44dc4..c6bb4f76f 100644 --- a/sys/auto_init/can/auto_init_can_native.c +++ b/sys/auto_init/can/auto_init_can_native.c @@ -40,6 +40,9 @@ void auto_init_can_native(void) { candev_linux_init(&candev_linux[i], &candev_linux_conf[i]); candev_dev_linux[i].dev = (candev_t *)&candev_linux[i]; candev_dev_linux[i].name = candev_linux_params[i].name; +#ifdef MODULE_CAN_TRX + candev_dev_linux[i].trx = candev_linux_params[i].trx; +#endif #ifdef MODULE_CAN_PM candev_dev_linux[i].rx_inactivity_timeout = candev_linux_params[i].rx_inactivity_timeout; candev_dev_linux[i].tx_wakeup_timeout = candev_linux_params[i].tx_wakeup_timeout; diff --git a/sys/can/device.c b/sys/can/device.c index eab6b7a05..ab159aaab 100644 --- a/sys/can/device.c +++ b/sys/can/device.c @@ -23,6 +23,10 @@ #include "can/pkt.h" #include "can/dll.h" +#ifdef MODULE_CAN_TRX +#include "can/can_trx.h" +#endif + #define ENABLE_DEBUG (0) #include "debug.h" @@ -131,6 +135,9 @@ static int power_up(candev_dev_t *candev_dev) DEBUG("candev: power up\n"); +#ifdef MODULE_CAN_TRX + can_trx_set_mode(candev_dev->trx, TRX_NORMAL_MODE); +#endif canopt_state_t state = CANOPT_STATE_ON; int res = dev->driver->set(dev, CANOPT_STATE, &state, sizeof(state)); dev->state = CAN_STATE_ERROR_ACTIVE; @@ -144,6 +151,9 @@ static int power_down(candev_dev_t *candev_dev) DEBUG("candev: power down\n"); +#ifdef MODULE_CAN_TRX + can_trx_set_mode(candev_dev->trx, TRX_SLEEP_MODE); +#endif canopt_state_t state = CANOPT_STATE_SLEEP; int res = dev->driver->set(dev, CANOPT_STATE, &state, sizeof(state)); dev->state = CAN_STATE_SLEEPING; @@ -209,6 +219,9 @@ static void *_can_device_thread(void *args) candev_dev->pm_timer.arg = candev_dev; pm_reset(candev_dev, candev_dev->rx_inactivity_timeout); #endif +#ifdef MODULE_CAN_TRX + can_trx_init(candev_dev->trx); +#endif int res; can_pkt_t *pkt; @@ -312,6 +325,20 @@ static void *_can_device_thread(void *args) reply.content.value = (uint32_t)res; msg_reply(&msg, &reply); break; +#ifdef MODULE_CAN_TRX + case CAN_MSG_SET_TRX: + DEBUG("can device: CAN_MSG_SET_TRX received\n"); + reply.type = CAN_MSG_ACK; + if (dev->state != CAN_STATE_SLEEPING) { + reply.content.value = -EBUSY; + } + else { + candev_dev->trx = msg.content.ptr; + reply.content.value = 0; + } + msg_reply(&msg, &reply); + break; +#endif #ifdef MODULE_CAN_PM case CAN_MSG_PM: DEBUG("can device: pm power down\n"); diff --git a/sys/can/dll.c b/sys/can/dll.c index 0287863d6..d3ac0f096 100644 --- a/sys/can/dll.c +++ b/sys/can/dll.c @@ -469,6 +469,23 @@ int raw_can_set_bitrate(int ifnum, uint32_t bitrate, uint32_t sample_point) return res; } +#ifdef MODULE_CAN_TRX +int raw_can_set_trx(int ifnum, can_trx_t *trx) +{ + msg_t msg, reply; + + assert(ifnum < candev_nb); + + msg.type = CAN_MSG_SET_TRX; + msg.content.ptr = trx; + if (msg_send_receive(&msg, &reply, candev_list[ifnum]->pid) != 1) { + return -EBUSY; + } + + return (int) reply.content.value; +} +#endif + int raw_can_get_ifnum_by_name(const char *name) { for (int i = 0; i < candev_nb; i++) { diff --git a/sys/include/can/common.h b/sys/include/can/common.h index 663d2a59f..38040fadc 100644 --- a/sys/include/can/common.h +++ b/sys/include/can/common.h @@ -91,6 +91,9 @@ enum can_msg { CAN_MSG_REMOVE_FILTER, /**< remove a filter */ CAN_MSG_POWER_UP, /**< power up */ CAN_MSG_POWER_DOWN, /**< power down */ +#if defined(MODULE_CAN_TRX) || defined(DOXYGEN) + CAN_MSG_SET_TRX, /**< set a transceiver */ +#endif /* candev internal messages */ CAN_MSG_EVENT = 0x200, /**< driver event */ CAN_MSG_WAKE_UP, /**< driver has been woken up by bus */ diff --git a/sys/include/can/device.h b/sys/include/can/device.h index f01dc4492..ba8c07b78 100644 --- a/sys/include/can/device.h +++ b/sys/include/can/device.h @@ -31,6 +31,9 @@ extern "C" { #ifdef MODULE_CAN_PM #include "xtimer.h" #endif +#ifdef MODULE_CAN_TRX +#include "can/can_trx.h" +#endif #ifndef CAN_MAX_RATE_ERROR /** @@ -52,6 +55,9 @@ extern "C" { */ typedef struct candev_params { const char *name; /**< candev name to set */ +#if defined(MODULE_CAN_TRX) || defined(DOXYGEN) + can_trx_t *trx; /**< transceiver to set */ +#endif #if defined(MODULE_CAN_PM) || defined(DOXYGEN) uint32_t rx_inactivity_timeout; /**< power management rx timeout value */ uint32_t tx_wakeup_timeout; /**< power management tx wake up value */ @@ -66,6 +72,9 @@ typedef struct candev_dev { int ifnum; /**< interface number */ kernel_pid_t pid; /**< pid */ const char *name; /**< device name */ +#if defined(MODULE_CAN_TRX) || defined(DOXYGEN) + can_trx_t *trx; /**< transceiver attached to the device */ +#endif #if defined(MODULE_CAN_PM) || defined(DOXYGEN) uint32_t rx_inactivity_timeout; /**< Min timeout loaded when a frame is received */ uint32_t tx_wakeup_timeout; /**< Min timeout loaded when a frame is sent */ diff --git a/sys/include/can/raw.h b/sys/include/can/raw.h index 1316bf966..4420be9d4 100644 --- a/sys/include/can/raw.h +++ b/sys/include/can/raw.h @@ -37,6 +37,9 @@ extern "C" { #include "mbox.h" #endif +#ifdef MODULE_TRX +#include "can/can_trx.h" +#endif /** * @brief Default value for undefined interface number @@ -253,6 +256,21 @@ candev_dev_t *raw_can_get_dev_by_ifnum(int ifnum); */ int raw_can_set_bitrate(int ifnum, uint32_t bitrate, uint32_t sample_point); +#if defined(MODULE_CAN_TRX) || defined(DOXYGEN) +/** + * @brief Set a transceiver for a given interface + * + * The interface must be powered down before changing the transceiver. + * + * @param[in] ifnum the interface number + * @param[in] trx the transceiver to set + * + * @return 0 on success + * @return < 0 on error (-EBUSY if device is not powered down) + */ +int raw_can_set_trx(int ifnum, can_trx_t *trx); +#endif + #ifdef __cplusplus } #endif diff --git a/tests/conn_can/Makefile b/tests/conn_can/Makefile index 3de0ceee9..48c0976d7 100644 --- a/tests/conn_can/Makefile +++ b/tests/conn_can/Makefile @@ -19,5 +19,6 @@ USEMODULE += conn_can USEMODULE += can_isotp USEMODULE += conn_can_isotp_multi USEMODULE += can_pm +USEMODULE += can_trx include $(RIOTBASE)/Makefile.include From 8ad59e35cf8fc38be954c4b4ac92b02ce53a4d88 Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Thu, 11 May 2017 18:26:00 +0200 Subject: [PATCH 9/9] sc: add CAN basic send and dump commands --- sys/shell/commands/Makefile | 3 + sys/shell/commands/sc_can.c | 184 ++++++++++++++++++++++++++++ sys/shell/commands/shell_commands.c | 7 ++ 3 files changed, 194 insertions(+) create mode 100644 sys/shell/commands/sc_can.c diff --git a/sys/shell/commands/Makefile b/sys/shell/commands/Makefile index 135bcc466..e55d36018 100644 --- a/sys/shell/commands/Makefile +++ b/sys/shell/commands/Makefile @@ -64,6 +64,9 @@ endif ifneq (,$(filter vfs,$(USEMODULE))) SRC += sc_vfs.c endif +ifneq (,$(filter conn_can,$(USEMODULE))) + SRC += sc_can.c +endif # TODO # Conditional building not possible at the moment due to diff --git a/sys/shell/commands/sc_can.c b/sys/shell/commands/sc_can.c new file mode 100644 index 000000000..61041b396 --- /dev/null +++ b/sys/shell/commands/sc_can.c @@ -0,0 +1,184 @@ +/* + * Copyright 2017 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_shell_commands + * @{ + * + * @file + * @brief Shell command implementation for CAN stack + * + * @author Vincent Dupont + * + * @} + */ + + +#include +#include +#include +#include + +#include "can/conn/raw.h" +#include "can/raw.h" + +#define SC_CAN_MAX_FILTERS 10 +#define xstr(a) str(a) +#define str(a) #a + +static int _can_usage(void); + +static int _list(int argc, char **argv) +{ + (void)argc; + (void)argv; + + for (int i = 0; i < CAN_DLL_NUMOF; i++) { + const char *name = raw_can_get_name_by_ifnum(i); + if (name) { + printf("CAN #%d: %s\n", i, name); + } + else { + break; + } + } + + return 0; +} + +static int _send(int argc, char **argv) +{ + if (argc < 3 || argc > 11) { + _can_usage(); + return 1; + } + + conn_can_raw_t conn; + struct can_frame frame; + int ifnum = atoi(argv[1]); + if (ifnum >= CAN_DLL_NUMOF) { + puts("Invalid ifnum"); + return 1; + } + frame.can_id = strtoul(argv[2], NULL, 16); + frame.can_dlc = argc - 3; + for (int i = 0; i < frame.can_dlc; i++) { + frame.data[i] = strtoul(argv[3 + i], NULL, 16); + } + conn_can_raw_create(&conn, NULL, 0, ifnum, 0); + conn_can_raw_send(&conn, &frame, 0); + + return 0; +} + +static int _dump(int argc, char **argv) +{ + if (argc < 4) { + _can_usage(); + return 0; + } + + int ret; + struct can_filter filters[SC_CAN_MAX_FILTERS]; + conn_can_raw_t conn; + struct can_frame frame; + int ifnum = atoi(argv[1]); + int cnt = atoi(argv[2]); + uint32_t ms = strtoul(argv[3], NULL, 0); + size_t nb_filters = 1; + if (ifnum >= CAN_DLL_NUMOF) { + puts("Invalid ifnum"); + return 1; + } + if (cnt) { + cnt++; + } + + if (argc > 4) { + char *p = argv[4]; + char *end; + int i = 0; + while (*p != '\0' && i < SC_CAN_MAX_FILTERS) { + filters[i].can_id = strtoul(p, &end, 16); + if (*end == ':') { + p = end + 1; + filters[i].can_mask = strtoul(p, &end, 16); + } + else { + filters[i].can_mask = 0xffffffff; + } + if (*end == ',') { + p = end + 1; + } + else { + p = end; + } + i++; + } + nb_filters = i; + } + else { + filters[0].can_id = 0; + filters[0].can_mask = 0; + } + conn_can_raw_create(&conn, filters, nb_filters, ifnum, 0); + while ((cnt != 1) && + ((ret = conn_can_raw_recv(&conn, &frame, ms * US_PER_MS)) + == sizeof(struct can_frame))) { + printf("%-8s(%d) %8" PRIX32 " [%x] ", + raw_can_get_name_by_ifnum(ifnum), ifnum, + frame.can_id, frame.can_dlc); + for (int i = 0; i < frame.can_dlc; i++) { + printf(" %02X", frame.data[i]); + } + printf("\n"); + if (cnt) { + cnt--; + } + } + if (ret < 0) { + puts("Timeout"); + } + puts("Closing"); + conn_can_raw_close(&conn); + + return 0; +} + +static int _can_usage(void) +{ + puts("usage: can [arguments]"); + puts("commands:"); + puts("\tlist"); + puts("\tsend ifnum id [B1 .. B8]"); + puts("\tdump ifnum nb ms [id1[:mask1][,id2[:mask2], .. id" + xstr(SC_CAN_MAX_FILTERS) ":[mask" xstr(SC_CAN_MAX_FILTERS) "]]"); + return 0; +} + +int _can_handler(int argc, char **argv) +{ + if (argc < 2) { + _can_usage(); + return 1; + } + else if (strncmp(argv[1], "list", 5) == 0) { + return _list(argc - 1, argv + 1); + } + else if (strncmp(argv[1], "send", 5) == 0) { + return _send(argc - 1, argv + 1); + } + else if (strncmp(argv[1], "dump", 5) == 0) { + return _dump(argc - 1, argv + 1); + } + else { + printf("unknown command: %s\n", argv[1]); + return 1; + } + return 0; +} diff --git a/sys/shell/commands/shell_commands.c b/sys/shell/commands/shell_commands.c index d72730051..8b4882f5a 100644 --- a/sys/shell/commands/shell_commands.c +++ b/sys/shell/commands/shell_commands.c @@ -132,6 +132,10 @@ extern int _vfs_handler(int argc, char **argv); extern int _ls_handler(int argc, char **argv); #endif +#ifdef MODULE_CONN_CAN +extern int _can_handler(int argc, char **argv); +#endif + const shell_command_t _shell_command_list[] = { {"reboot", "Reboot the node", _reboot_handler}, #ifdef MODULE_CONFIG @@ -220,6 +224,9 @@ const shell_command_t _shell_command_list[] = { #ifdef MODULE_VFS {"vfs", "virtual file system operations", _vfs_handler}, {"ls", "list files", _ls_handler}, +#endif +#ifdef MODULE_CONN_CAN + {"can", "CAN commands", _can_handler}, #endif {NULL, NULL, NULL} };