/* * 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 sys_can_conn * @{ * @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); xtimer_remove(&timer); switch (msg.type) { case CAN_MSG_TX_ERROR: return -EIO; case CAN_MSG_TX_CONFIRMATION: 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"); raw_can_abort(conn->ifnum, handle); 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; } xtimer_set(&timer, CONN_CAN_RAW_TIMEOUT_TX_CONF); 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; }