From ab5f8548f2574b5057af314caa2b4111eed5bb14 Mon Sep 17 00:00:00 2001 From: Vincent Dupont <vincent@otakeys.com> Date: Wed, 23 Nov 2016 19:10:34 +0100 Subject: [PATCH] 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 d4706ae620..43dec2a261 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 003fad8198..83a3d62ae5 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 449d4878ca..f6f3dc0f41 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 0000000000..22c56214a4 --- /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 0000000000..16b7328275 --- /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 <vincent@otakeys.com> + */ + +#ifdef MODULE_CAN_ISOTP +#include <errno.h> +#include <string.h> + +#include "can/conn/isotp.h" +#include "can/isotp.h" +#include "can/device.h" + +#ifdef MODULE_CONN_CAN_ISOTP_MULTI +#include "utlist.h" +#endif + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include "xtimer.h" + +#define _TIMEOUT_TX_MSG_TYPE (0x8000) +#define _TIMEOUT_RX_MSG_TYPE (0x8001) +#define _CLOSE_CONN_MSG_TYPE (0x8002) +#define _TIMEOUT_MSG_VALUE (0xABCDEFAB) + +#ifndef CONN_CAN_ISOTP_TIMEOUT_TX_CONF +#define CONN_CAN_ISOTP_TIMEOUT_TX_CONF (10 * US_PER_SEC) +#endif + +static inline void put_msg(conn_can_isotp_t *conn, msg_t *msg) +{ +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + mbox_put(&conn->master->mbox, msg); +#else + mbox_put(&conn->mbox, msg); +#endif +} + +static inline void get_msg(conn_can_isotp_t *conn, msg_t *msg) +{ +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + mbox_get(&conn->master->mbox, msg); +#else + mbox_get(&conn->mbox, msg); +#endif +} + +int conn_can_isotp_create(conn_can_isotp_t *conn, struct isotp_options *options, int ifnum) +{ + assert(conn != NULL); + assert(options != NULL); + assert(ifnum < CAN_DLL_NUMOF); + +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + DEBUG("conn_can_isotp_create: conn=%p, conn->master=%p, ifnum=%d\n", + (void *)conn, (void *)conn->master, ifnum); + + if (conn->master == conn || conn->master == NULL) { + conn->master = conn; + conn->master->next = NULL; + mutex_init(&conn->master->lock); + mutex_lock(&conn->master->lock); + DEBUG("conn_can_isotp_create: init master conn\n"); + mbox_init(&conn->master->mbox, conn->master->mbox_queue, CONN_CAN_ISOTP_MBOX_SIZE); + mutex_unlock(&conn->master->lock); + } +#else + mbox_init(&conn->mbox, conn->mbox_queue, CONN_CAN_ISOTP_MBOX_SIZE); +#endif + + conn->ifnum = ifnum; + + memset(&conn->isotp, 0, sizeof(struct isotp)); + conn->isotp.opt = *options; + + return 0; +} + +int conn_can_isotp_bind(conn_can_isotp_t *conn) +{ + assert(conn != NULL); + assert(conn->isotp.opt.tx_id != 0 || conn->isotp.opt.rx_id != 0); + + DEBUG("conn_can_isotp_bind: conn=%p, ifnum=%d\n", + (void *)conn, conn->ifnum); + + if (conn->bound) { + return -EALREADY; + } + msg_t msg; + int ret; + can_reg_entry_t entry; + entry.ifnum = conn->ifnum; + entry.type = CAN_TYPE_MBOX; +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + assert(conn->master != NULL); + + entry.target.mbox = &(conn->master->mbox); + if (conn != conn->master) { + mutex_lock(&conn->master->lock); + LL_APPEND(conn->master->next, (conn_can_isotp_slave_t *)conn); + mutex_unlock(&conn->master->lock); + } + ret = mbox_try_get(&conn->master->mbox, &msg); +#else + entry.target.mbox = &conn->mbox; + ret = mbox_try_get(&conn->mbox, &msg); +#endif + if ((ret == 1) && (msg.type != _CLOSE_CONN_MSG_TYPE)) { + DEBUG("conn_can_isotp_bind: msg in queue type=%x\n", msg.type); + put_msg(conn, &msg); + } + + ret = isotp_bind(&conn->isotp, &entry, conn); + if (!ret) { + conn->bound = 1; + } + return ret; +} + +static void _tx_conf_timeout(void *arg) +{ + conn_can_isotp_t *conn = arg; + msg_t msg; + + msg.type = _TIMEOUT_TX_MSG_TYPE; + msg.content.value = _TIMEOUT_MSG_VALUE; + + put_msg(conn, &msg); +} + +int conn_can_isotp_send(conn_can_isotp_t *conn, const void *buf, size_t size, int flags) +{ + assert(conn != NULL); + assert(buf != NULL || size == 0); + + int ret = 0; + + if (!conn->bound) { + return -ENOTCONN; + } + + if (flags & CAN_ISOTP_TX_DONT_WAIT) { + return isotp_send(&conn->isotp, buf, size, flags); + } + else { + xtimer_t timer; + timer.callback = _tx_conf_timeout; + timer.arg = conn; + xtimer_set(&timer, CONN_CAN_ISOTP_TIMEOUT_TX_CONF); + + ret = isotp_send(&conn->isotp, buf, size, flags); + + msg_t msg; + while (1) { + get_msg(conn, &msg); + switch (msg.type) { + case CAN_MSG_TX_ERROR: + if (msg.content.ptr == conn) { + ret = -EIO; + } + /* No break */ + case CAN_MSG_TX_CONFIRMATION: +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + if (msg.content.ptr != conn) { + mbox_put(&conn->master->mbox, &msg); + break; + } +#endif + xtimer_remove(&timer); + return ret; + case _TIMEOUT_TX_MSG_TYPE: + return -ETIMEDOUT; + default: + DEBUG("conn_can_isotp_send: unexpected msg %x, requeing\n", msg.type); + put_msg(conn, &msg); + break; + } + } + } + + return ret; +} + +static void _rx_timeout(void *arg) +{ + conn_can_isotp_t *conn = arg; + msg_t msg; + + msg.type = _TIMEOUT_RX_MSG_TYPE; + msg.content.value = _TIMEOUT_MSG_VALUE; + + put_msg(conn, &msg); +} + +int conn_can_isotp_recv(conn_can_isotp_t *conn, void *buf, size_t size, uint32_t timeout) +{ + assert(conn != NULL); + assert(buf != NULL); + + int ret = 0; + gnrc_pktsnip_t *snip; + + if (!conn->bound) { + return -ENOTCONN; + } + +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + if (conn->rx) { + snip = conn->rx->data.iov_base; + if (snip->size <= size) { + memcpy(buf, snip->data, snip->size); + ret = snip->size; + } + else { + ret = -EOVERFLOW; + } + isotp_free_rx(conn->rx); + conn->rx = NULL; + return ret; + } +#endif + + xtimer_t timer; + if (timeout != 0) { + timer.callback = _rx_timeout; + timer.arg = conn; + xtimer_set(&timer, timeout); + } + + msg_t msg; + can_rx_data_t *rx; + + while (1) { + get_msg(conn, &msg); + switch (msg.type) { + case CAN_MSG_RX_INDICATION: + DEBUG("conn_can_isotp_recv: CAN_MSG_RX_INDICATION\n"); + rx = msg.content.ptr; + snip = rx->data.iov_base; +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + if (rx->arg != conn) { + mbox_put(&conn->master->mbox, &msg); + break; + } +#endif + if (timeout != 0) { + xtimer_remove(&timer); + } + if (snip->size <= size) { + memcpy(buf, snip->data, snip->size); + ret = snip->size; + } + else { + ret = -EOVERFLOW; + } + isotp_free_rx(rx); + return ret; + case _TIMEOUT_RX_MSG_TYPE: + DEBUG("conn_can_isotp_recv: _TIMEOUT_RX_MSG_TYPE\n"); + if (msg.content.value == _TIMEOUT_MSG_VALUE) { + ret = -ETIMEDOUT; + } + else { + ret = -EINTR; + } + return ret; + case _CLOSE_CONN_MSG_TYPE: + DEBUG("conn_can_isotp_recv: _CLOSE_CONN_MSG_TYPE\n"); +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + if ((msg.content.ptr == conn) || (msg.content.ptr == conn->master)) { +#endif + if (timeout != 0) { + xtimer_remove(&timer); + } + return -ECONNABORTED; +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + } +#endif + break; + default: + DEBUG("conn_can_isotp_recv: unexpected msg %x\n", msg.type); + if (timeout != 0) { + xtimer_remove(&timer); + } + ret = -EINTR; + return ret; + } + } + + return ret; +} + +int conn_can_isotp_close(conn_can_isotp_t *conn) +{ + assert(conn != NULL); + msg_t msg; + + DEBUG("conn_can_isotp_close: conn=%p, ifnum=%d\n", + (void *)conn, conn->ifnum); + + if (!conn->bound) { + return -EALREADY; + } + +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + assert(conn->master != NULL); + + if (conn->master != conn) { + mutex_lock(&conn->master->lock); + LL_DELETE(conn->master->next, (conn_can_isotp_slave_t *)conn); + mutex_unlock(&conn->master->lock); + } + else { + if (conn->master->next) { + return -EBUSY; + } + } +#endif + + isotp_release(&conn->isotp); + +#ifdef MODULE_CONN_CAN_ISOTP_MULTI + if (conn->rx) { + isotp_free_rx(conn->rx); + } + if (conn->master == conn) { + while (mbox_try_get(&conn->master->mbox, &msg)) { + if (msg.type == CAN_MSG_RX_INDICATION) { + DEBUG("conn_can_isotp_close: freeing %p\n", msg.content.ptr); + isotp_free_rx(msg.content.ptr); + } + } + } +#else + while (mbox_try_get(&conn->mbox, &msg)) { + if (msg.type == CAN_MSG_RX_INDICATION) { + DEBUG("conn_can_isotp_close: freeing %p\n", msg.content.ptr); + isotp_free_rx(msg.content.ptr); + } + } +#endif + + msg.type = _CLOSE_CONN_MSG_TYPE; + msg.content.ptr = conn; + put_msg(conn, &msg); + + conn->bound = 0; + + return 0; +} + +#ifdef MODULE_CONN_CAN_ISOTP_MULTI +int conn_can_isotp_select(conn_can_isotp_slave_t **conn, conn_can_isotp_t *master, uint32_t timeout) +{ + assert(master != NULL); + assert(conn != NULL); + + int ret; + + xtimer_t timer; + if (timeout != 0) { + timer.callback = _rx_timeout; + timer.arg = master; + xtimer_set(&timer, timeout); + } + + msg_t msg; + can_rx_data_t *rx; + + mbox_get(&master->mbox, &msg); + + if (timeout != 0) { + xtimer_remove(&timer); + } + switch (msg.type) { + case CAN_MSG_RX_INDICATION: + DEBUG("conn_can_isotp_select: CAN_MSG_RX_INDICATION\n"); + rx = msg.content.ptr; + *conn = rx->arg; + (*conn)->rx = rx; + ret = 0; + break; + case _TIMEOUT_RX_MSG_TYPE: + DEBUG("conn_can_isotp_select: _TIMEOUT_MSG_VALUE\n"); + if (msg.content.value == _TIMEOUT_MSG_VALUE) { + ret = -ETIMEDOUT; + } + else { + ret = -EINTR; + } + *conn = NULL; + break; + default: + DEBUG("conn_can_isotp_select: %d\n", msg.type); + *conn = NULL; + ret = -EINTR; + break; + } + + return ret; +} +#endif /* MODULE_CONN_CAN_ISOTP_MULTI */ + +#else +typedef int dont_be_pedantic; +#endif /* MODULE_CAN_ISOTP */ diff --git a/sys/can/conn/raw.c b/sys/can/conn/raw.c new file mode 100644 index 0000000000..6d3678ddca --- /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 <vincent@otakeys.com> + */ + +#include <errno.h> +#include <string.h> + +#include "can/conn/raw.h" +#include "can/can.h" +#include "can/raw.h" +#include "timex.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include "xtimer.h" + +#define _TIMEOUT_TX_MSG_TYPE (0x8000) +#define _TIMEOUT_RX_MSG_TYPE (0x8001) +#define _CLOSE_CONN_MSG_TYPE (0x8002) +#define _TIMEOUT_MSG_VALUE (0xABCDEFAB) + +#ifndef CONN_CAN_RAW_TIMEOUT_TX_CONF +#define CONN_CAN_RAW_TIMEOUT_TX_CONF (1 * US_PER_SEC) +#endif + +int conn_can_raw_create(conn_can_raw_t *conn, struct can_filter *filter, size_t count, + int ifnum, int flags) +{ + assert(conn != NULL); + assert(ifnum < CAN_DLL_NUMOF); + + DEBUG("conn_can_raw_create: create conn=%p, ifnum=%d flags=%d\n", (void *)conn, ifnum, flags); + + mbox_init(&conn->mbox, conn->mbox_queue, CONN_CAN_RAW_MBOX_SIZE); + conn->flags = flags; + conn->count = 0; + conn->ifnum = ifnum; + + if (flags & CONN_CAN_RECVONLY) { + can_opt_t opt; + opt.opt = CANOPT_STATE; + canopt_state_t state = CANOPT_STATE_LISTEN_ONLY; + opt.data = &state; + opt.data_len = sizeof(state); + int ret = raw_can_set_can_opt(ifnum, &opt); + if (ret < 0) { + return ret; + } + } + + return conn_can_raw_set_filter(conn, filter, count); +} + + +int conn_can_raw_set_filter(conn_can_raw_t *conn, struct can_filter *filter, size_t count) +{ + assert(conn != NULL); + assert(filter != NULL || count == 0); + + DEBUG("conn_can_raw_set_filter: conn=%p, filter=%p, count=%d\n", + (void *)conn, (void *)filter, count); + DEBUG("conn_can_raw_set_filter: conn->filter=%p, conn->count=%d\n", + (void *)conn->filter, conn->count); + + /* unset previous filters */ + if (conn->count) { + for (size_t i = 0; i < conn->count; i++) { + DEBUG("conn_can_raw_set_filter: unsetting filter=0x%" PRIx32 ", mask=0x%" PRIx32 "\n", + conn->filter[i].can_id, conn->filter[i].can_mask); + raw_can_unsubscribe_rx_mbox(conn->ifnum, &conn->filter[i], &conn->mbox, conn); + } + } + + for (size_t i = 0; i < count; i++) { + DEBUG("conn_can_raw_set_filter: setting filter=0x%" PRIx32 ", mask=0x%" PRIx32 "\n", + filter[i].can_id, filter[i].can_mask); + int ret = raw_can_subscribe_rx_mbox(conn->ifnum, &filter[i], &conn->mbox, conn); + if (ret < 0) { + DEBUG("conn_can_raw_set_filter: error setting filters %d\n", ret); + for (size_t j = 0; j < i; j++) { + DEBUG("conn_can_raw_set_filter: unsetting filter=0x%" PRIx32 ", mask=0x%" PRIx32 "\n", + filter[j].can_id, filter[j].can_mask); + raw_can_unsubscribe_rx_mbox(conn->ifnum, &filter[j], &conn->mbox, conn); + } + return ret; + } + } + + conn->filter = filter; + conn->count = count; + + return 0; +} + +static void _tx_conf_timeout(void *arg) +{ + conn_can_raw_t *conn = arg; + msg_t msg; + + msg.type = _TIMEOUT_TX_MSG_TYPE; + msg.content.value = _TIMEOUT_MSG_VALUE; + + mbox_put(&conn->mbox, &msg); +} + +int conn_can_raw_send(conn_can_raw_t *conn, const struct can_frame *frame, int flags) +{ + assert(conn != NULL); + assert(conn->ifnum < CAN_DLL_NUMOF); + assert((conn->flags & CONN_CAN_RECVONLY) == 0); + assert(frame != NULL); + + int ret = 0; + int handle; + + DEBUG("conn_can_raw_send: conn=%p, frame=%p, flags=%d\n", + (void *)conn, (void *)frame, flags); + + if (flags & CONN_CAN_DONTWAIT) { + handle = ret = raw_can_send(conn->ifnum, frame, 0); + if (ret >= 0) { + ret = 0; + } + } + else { + xtimer_t timer; + timer.callback = _tx_conf_timeout; + timer.arg = conn; + xtimer_set(&timer, CONN_CAN_RAW_TIMEOUT_TX_CONF); + + handle = raw_can_send_mbox(conn->ifnum, frame, &conn->mbox); + if (handle < 0) { + xtimer_remove(&timer); + return handle; + } + + msg_t msg; + int timeout = 5; + while (1) { + mbox_get(&conn->mbox, &msg); + switch (msg.type) { + case CAN_MSG_TX_ERROR: + xtimer_remove(&timer); + return -EIO; + case CAN_MSG_TX_CONFIRMATION: + xtimer_remove(&timer); + if ((int)msg.content.value == handle) { + DEBUG("conn_can_raw_send: frame sent correctly\n"); + return 0; + } + else { + raw_can_abort(conn->ifnum, handle); + return -EINTR; + } + break; + case _TIMEOUT_TX_MSG_TYPE: + DEBUG("conn_can_raw_send: timeout\n"); + return -ETIMEDOUT; + break; + default: + DEBUG("conn_can_raw_send: unexpected msg=%x, requeing\n", msg.type); + mbox_put(&conn->mbox, &msg); + if (!timeout--) { + return -EINTR; + } + break; + } + } + } + + return ret; +} + +static void _rx_timeout(void *arg) +{ + conn_can_raw_t *conn = arg; + msg_t msg; + + msg.type = _TIMEOUT_RX_MSG_TYPE; + msg.content.value = _TIMEOUT_MSG_VALUE; + + mbox_put(&conn->mbox, &msg); +} + +int conn_can_raw_recv(conn_can_raw_t *conn, struct can_frame *frame, uint32_t timeout) +{ + assert(conn != NULL); + assert(conn->ifnum < CAN_DLL_NUMOF); + assert(frame != NULL); + + xtimer_t timer; + + if (timeout != 0) { + timer.callback = _rx_timeout; + timer.arg = conn; + xtimer_set(&timer, timeout); + } + + int ret; + msg_t msg; + can_rx_data_t *rx; + + mbox_get(&conn->mbox, &msg); + if (timeout != 0) { + xtimer_remove(&timer); + } + switch (msg.type) { + case CAN_MSG_RX_INDICATION: + DEBUG("conn_can_raw_recv: CAN_MSG_RX_INDICATION\n"); + rx = msg.content.ptr; + memcpy(frame, rx->data.iov_base, rx->data.iov_len); + ret = rx->data.iov_len; + raw_can_free_frame(rx); + break; + case _TIMEOUT_RX_MSG_TYPE: + if (msg.content.value == _TIMEOUT_MSG_VALUE) { + ret = -ETIMEDOUT; + } + else { + ret = -EINTR; + } + break; + case _CLOSE_CONN_MSG_TYPE: + if (msg.content.ptr == conn) { + ret = -ECONNABORTED; + } + else { + ret = -EINTR; + } + break; + default: + mbox_put(&conn->mbox, &msg); + ret = -EINTR; + break; + } + + return ret; +} + +int conn_can_raw_close(conn_can_raw_t *conn) +{ + assert(conn != NULL); + assert(conn->ifnum < CAN_DLL_NUMOF); + + DEBUG("conn_can_raw_close: conn=%p\n", (void *)conn); + + if (conn->count) { + for (size_t i = 0; i < conn->count; i++) { + DEBUG("conn_can_raw_close: unsetting filter=0x%" PRIx32 ", mask=0x%" PRIx32 "\n", + conn->filter[i].can_id, conn->filter[i].can_mask); + raw_can_unsubscribe_rx_mbox(conn->ifnum, &conn->filter[i], &conn->mbox, conn); + } + conn->count = 0; + msg_t msg; + while (mbox_try_get(&conn->mbox, &msg)) { + if (msg.type == CAN_MSG_RX_INDICATION) { + DEBUG("conn_can_raw_close: incoming msg pending, freeing\n"); + raw_can_free_frame(msg.content.ptr); + } + } + msg.type = _CLOSE_CONN_MSG_TYPE; + msg.content.ptr = conn; + mbox_try_put(&conn->mbox, &msg); + } + + return 0; +} diff --git a/sys/include/can/conn/isotp.h b/sys/include/can/conn/isotp.h new file mode 100644 index 0000000000..e0139aa3f8 --- /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 <vincent@otakeys.com> + * + */ + +#ifndef CAN_CONN_ISOTP_H +#define CAN_CONN_ISOTP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "can/can.h" +#include "can/isotp.h" +#include "mbox.h" + +#if defined(MODULE_CONN_CAN_ISOTP_MULTI) || defined(DOXYGEN) +#include "mutex.h" + +#ifndef CONN_CAN_ISOTP_MBOX_SIZE +/** + * @brief Mailbox size of a conn_can_isotp_t + */ +#define CONN_CAN_ISOTP_MBOX_SIZE (16) +#endif + +/** + * @brief ISO-TP connection + * + * When conn_can_isotp_multi module is used, this is a 'master' connection + * which can be used to send and receive with multiple connections within + * a single thread. + * + * If conn_can_isotp_multi is not used, this is a simple ISO-TP connection + */ +typedef struct conn_can_isotp_master conn_can_isotp_t; + +/** + * @brief ISO-TP salve connection + * + * This is a slave connection which exists only when conn_can_isotp_multi + * module is used. + */ +typedef struct conn_can_isotp_slave { + struct conn_can_isotp_slave *next; /**< Next slave in the list */ + struct conn_can_isotp_master *master; /**< Master connection holding the mailbox */ + struct isotp isotp; /**< ISO-TP parameters and status */ + int ifnum; /**< interface number */ + int bound; /**< 1 if connection is bound */ + can_rx_data_t *rx; /**< Buffered rx data */ +} conn_can_isotp_slave_t; + +/** + * @brief ISO-TP master connection + */ +struct conn_can_isotp_master { + /* slave fields */ + struct conn_can_isotp_slave *next; /**< First slave in the list */ + struct conn_can_isotp_master *master; /**< Master connection */ + struct isotp isotp; /**< ISO-TP parameters and status */ + int ifnum; /**< interface number */ + int bound; /**< 1 if connection is bound */ + can_rx_data_t *rx; /**< Buffered rx data */ + /* slave fields end */ + mutex_t lock; /**< Master lock */ + mbox_t mbox; /**< mailbox for the connection list */ + /** Connection list message queue */ + msg_t mbox_queue[CONN_CAN_ISOTP_MBOX_SIZE]; +}; + +/** + * @brief Initialize a slave connection + * + * This initializes a slave connection. + * + * This must be called on slave connections when conn_can_isotp_multi is used. + * Does not exist otherwise. + * + * @param[in] master the master connection + * @param[inout] slave the slave connection to initialize + */ +static inline void conn_can_isotp_init_slave(conn_can_isotp_t *master, conn_can_isotp_slave_t *slave) +{ + slave->next = NULL; + slave->master = master; + slave->rx = NULL; +} +#else + +#ifndef CONN_CAN_ISOTP_MBOX_SIZE +/** + * @brief Mailbox size of a conn_can_isotp_t + */ +#define CONN_CAN_ISOTP_MBOX_SIZE (16) +#endif + +/** + * @brief ISOTP connection + */ +typedef struct conn_can_isotp { + struct isotp isotp; /**< ISO-TP connection */ + int ifnum; /**< interface number */ + int bound; /**< 1 if connection is bound */ + mbox_t mbox; /**< mbox */ + /** message queue */ + msg_t mbox_queue[CONN_CAN_ISOTP_MBOX_SIZE]; +} conn_can_isotp_t; +#endif /* MODULE_CONN_CAN_ISOTP_MULTI */ + +/** + * @brief Create can isotp connection socket + * + * @param[inout] conn ISO-TP connection + * @param[in] options ISO-TP options + * @param[in] ifnum can device Interface + * + * @return 0 if socket was successfully connected + * @return any other negative number in case of an error + */ +int conn_can_isotp_create(conn_can_isotp_t *conn, struct isotp_options *options, int ifnum); + +/** + * @brief Bind a can isotp connection + * + * @param[inout] conn ISO-TP connection + * + * @return 0 on success + * @return any other negative number in case of an error + */ +int conn_can_isotp_bind(conn_can_isotp_t *conn); + +/** + * @brief Close can isotp connection socket + * + * @param[in] conn ISO-TP connection + * + * @return 0 if conn is closed correctly + * @return any other negative number in case of an error + */ +int conn_can_isotp_close(conn_can_isotp_t *conn); + +/** + * @brief Receive isotp data + * + * @param[in] conn ISO-TP connection + * @param[out] buf buf to fill in with received data + * @param[in] size size of the buffer in bytes + * @param[in] timeout timeout in us, 0 for infinite + * + * @return the number of bytes received + * @return any other negative number in case of an error + */ +int conn_can_isotp_recv(conn_can_isotp_t *conn, void *buf, size_t size, uint32_t timeout); + +/** + * @brief Generic can send + * + * @param[in] conn ISO-TP connection + * @param[in] buf data to send + * @param[in] size size of the buffer in bytes + * @param[in] flags make function blocked or not + * (CAN_ISOTP_TX_DONT_WAIT to ignore tx confirmation) + * + * @return the number of bytes sent + * @return any other negative number in case of an error + */ +int conn_can_isotp_send(conn_can_isotp_t *conn, const void *buf, size_t size, int flags); + +#if defined(MODULE_CONN_CAN_ISOTP_MULTI) || defined(DOXYGEN) +/** + * @brief Wait for reception from multiple connections + * + * @param[out] conn ISO-TP connection which received data + * @param[in] master the master connection + * @param[in] timeout timeout in us, 0 for infinite wait + * + * @return 0 if OK, < 0 if error + */ +int conn_can_isotp_select(conn_can_isotp_slave_t **conn, conn_can_isotp_t *master, uint32_t timeout); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CAN_CONN_ISOTP_H */ +/** @} */ diff --git a/sys/include/can/conn/raw.h b/sys/include/can/conn/raw.h new file mode 100644 index 0000000000..fee1fbc066 --- /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 <vincent@otakeys.com> + * + */ + +#ifndef CAN_CONN_RAW_H +#define CAN_CONN_RAW_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "can/can.h" +#include "can/raw.h" +#include "mbox.h" + +#ifndef CONN_CAN_RAW_MBOX_SIZE +/** + * @brief Mailbox size of a conn_can_raw_t + */ +#define CONN_CAN_RAW_MBOX_SIZE (16) +#endif + +/** + * @name flags values + * @{ + */ +#define CONN_CAN_DONTWAIT (1) /**< Do not wait for Tx confirmation when sending */ +#define CONN_CAN_RECVONLY (2) /**< Do not send anything on the bus */ +/** @} */ + +/** + * @brief RAW CAN connection + */ +typedef struct conn_can_raw { + int ifnum; /**< Interface number of the can device */ + int flags; /**< Config flags for that conn object */ + size_t count; /**< number of filters set */ + struct can_filter *filter; /**< list of filter */ + mbox_t mbox; /**< mbox */ + /** + * message queue + */ + msg_t mbox_queue[CONN_CAN_RAW_MBOX_SIZE]; +} conn_can_raw_t; + +/** + * @brief Create can connection socket + * + * @param[inout] conn CAN connection + * @param[in] filter list of filters to set + * @param[in] count number of filters in @p filter + * @param[in] ifnum can device Interface + * @param[in] flags conn flags to set (CONN_CAN_RECVONLY) + * + * @return 0 if socket was successfully connected + * @return any other negative number in case of an error + */ +int conn_can_raw_create(conn_can_raw_t *conn, struct can_filter *filter, size_t count, + int ifnum, int flags); + +/** + * @brief Close can connection socket + * + * @param[in] conn CAN connection + * + * @return 0 if conn is closed correctly + * @return any other negative number in case of an error. + */ +int conn_can_raw_close(conn_can_raw_t *conn); + +/** + * @brief Generic can receive + * + * @param[in] conn CAN connection + * @param[out] frame CAN frame to receive + * @param[in] timeout timeout in us, 0 for infinite + * + * @return the number of bytes received + * @return any other negative number in case of an error + */ +int conn_can_raw_recv(conn_can_raw_t *conn, struct can_frame *frame, uint32_t timeout); + +/** + * @brief Generic can send + * + * @param[in] conn CAN connection + * @param[in] frame frame to send + * @param[in] flags make function blocked or not + * (CONN_CAN_DONTWAIT to ignore tx confirmation) + * + * @return the number of bytes sent + * @return any other negative number in case of an error + */ +int conn_can_raw_send(conn_can_raw_t *conn, const struct can_frame *frame, int flags); + +/** + * @brief Set raw CAN filters + * + * If filters were already set for this connection, it first unsets the previous filters + * and sets the new ones. + * + * @param[in] conn CAN connection + * @param[in] filter list of filters to set + * @param[in] count number of filters in @p filter + * + * @return 0 if can filters were successfully set + * @return any other negative number in case of an error + */ +int conn_can_raw_set_filter(conn_can_raw_t *conn, struct can_filter *filter, size_t count); + + +#ifdef __cplusplus +} +#endif + +#endif /* CAN_CONN_RAW_H */ +/** @} */ -- GitLab