
committed by
GitHub

45 changed files with 7196 additions and 2 deletions
@ -0,0 +1,3 @@ |
|||
MODULE = can_linux |
|||
|
|||
include $(RIOTBASE)/Makefile.base |
@ -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 <hermann@otakeys.com> |
|||
* @author Aurelien Gonce <aurelien.gonce@altran.com> |
|||
* @author Vincent Dupont <vincent@otakeys.com> |
|||
* @} |
|||
*/ |
|||
|
|||
#if !defined(__linux__) |
|||
#error "MODULE can_linux is only available on Linux" |
|||
#else |
|||
|
|||
#include <string.h> |
|||
#include <errno.h> |
|||
#include <sys/ioctl.h> |
|||
#include <net/if.h> |
|||
|
|||
#include <linux/can/raw.h> |
|||
#include <linux/can/error.h> |
|||
|
|||
#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__) */ |
@ -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 <hermann@otakeys.com> |
|||
* @author Aurelien Gonce <aurelien.gonce@altran.com> |
|||
* @author Vincent Dupont <vincent@otakeys.com> |
|||
* @} |
|||
*/ |
|||
|
|||
#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 <stdbool.h> |
|||
|
|||
#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 */ |
@ -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 <vincent@otakeys.com> |
|||
* @} |
|||
*/ |
|||
|
|||
#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 */ |
|||
/** @} */ |
@ -0,0 +1 @@ |
|||
include $(RIOTBASE)/Makefile.base |
@ -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 <vincent@otakeys.com> |
|||
*/ |
|||
|
|||
#include <errno.h> |
|||
#include <stdlib.h> |
|||
|
|||
#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; |
|||
} |
|||
} |
@ -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 <aurelien.gonce@altran.com> |
|||
* @author Vincent Dupont <vincent@otakeys.com> |
|||
*/ |
|||
#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 */ |
|||
/** @} */ |
@ -0,0 +1,182 @@ |
|||
/*
|
|||
* Copyright (C) 2016 OTA keys S.A. |
|||
* |
|||
* This file is subject to the terms and conditions of the GNU Lesser General |
|||
* Public License v2.1. See the file LICENSE in the top level directory for |
|||
* more details. |
|||
*/ |
|||
|
|||
/**
|
|||
* @ingroup can |
|||
* @ingroup drivers |
|||
* @defgroup drivers_can CAN drivers |
|||
* @{ |
|||
* |
|||
* This is the CAN controller driver interface |
|||
* |
|||
* @file |
|||
* @brief Definitions low-level CAN driver interface |
|||
* |
|||
* @author Vincent Dupont <vincent@otakeys.com> |
|||
* @author Toon Stegen <toon.stegen@altran.com> |
|||
*/ |
|||
|
|||
#ifndef CAN_CANDEV_H |
|||
#define CAN_CANDEV_H |
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" { |
|||
#endif |
|||
|
|||
#include <stdint.h> |
|||
#include <stdlib.h> |
|||
|
|||
#include "can/can.h" |
|||
#include "can/common.h" |
|||
#include "mutex.h" |
|||
|
|||
|
|||
/**
|
|||
* @brief Possible event types that are send from the device driver to the |
|||
* upper layer |
|||
*/ |
|||
typedef enum { |
|||
CANDEV_EVENT_NOEVENT, /**< no event, used internally */ |
|||
CANDEV_EVENT_ISR, /**< driver needs it's ISR handled */ |
|||
CANDEV_EVENT_WAKE_UP, /**< driver has been woken up by bus */ |
|||
CANDEV_EVENT_TX_CONFIRMATION, /**< a packet has been sent */ |
|||
CANDEV_EVENT_TIMEOUT_TX_CONF, /**< tx conf timeout received */ |
|||
CANDEV_EVENT_RX_INDICATION, /**< a packet has been received */ |
|||
CANDEV_EVENT_TX_ERROR, /**< there was an error when transmitting */ |
|||
CANDEV_EVENT_RX_ERROR, /**< there was an error when receiving */ |
|||
CANDEV_EVENT_BUS_OFF, /**< bus-off detected */ |
|||
CANDEV_EVENT_ERROR_PASSIVE, /**< driver switched in error passive */ |
|||
CANDEV_EVENT_ERROR_WARNING, /**< driver reached error warning */ |
|||
/* expand this list if needed */ |
|||
} candev_event_t; |
|||
|
|||
/**
|
|||
* @brief Forward declaration for candev struct |
|||
*/ |
|||
typedef struct candev candev_t; |
|||
|
|||
/**
|
|||
* @brief Event callback for signaling event to upper layers |
|||
* |
|||
* @param[in] dev CAN device descriptor |
|||
* @param[in] type type of the event |
|||
* @param[in] arg event argument |
|||
*/ |
|||
typedef void (*candev_event_cb_t)(candev_t *dev, candev_event_t event, void *arg); |
|||
|
|||
/**
|
|||
* @brief Structure to hold driver state |
|||
* |
|||
* Supposed to be extended by driver implementations. |
|||
* The extended structure should contain all variable driver state. |
|||
*/ |
|||
struct candev { |
|||
const struct candev_driver *driver; /**< ptr to that driver's interface. */ |
|||
candev_event_cb_t event_callback; /**< callback for device events */ |
|||
void *isr_arg; /**< argument to pass on isr event */ |
|||
struct can_bittiming bittiming; /**< device bittimings */ |
|||
enum can_state state; /**< device state */ |
|||
}; |
|||
|
|||
/**
|
|||
* @brief Structure to hold driver interface -> function mapping |
|||
*/ |
|||
typedef struct candev_driver { |
|||
/**
|
|||
* @brief Send packet |
|||
* |
|||
* @param[in] dev CAN device descriptor |
|||
* @param[in] frame CAN frame to send |
|||
* |
|||
* @return < 0 on error |
|||
* @return mailbox id >= 0 if OK |
|||
*/ |
|||
int (*send)(candev_t *dev, const struct can_frame *frame); |
|||
|
|||
/**
|
|||
* @brief Abort a packet sending |
|||
* |
|||
* @param[in] dev CAN device descriptor |
|||
* @param[in] frame CAN frame to abort |
|||
* |
|||
* @return < 0 on error |
|||
* @return 0 on OK |
|||
*/ |
|||
int (*abort)(candev_t *dev, const struct can_frame *frame); |
|||
|
|||
/**
|
|||
* @brief the driver's initialization function |
|||
* |
|||
* @param[in] dev CAN device descriptor |
|||
* |
|||
* @return < 0 on error, 0 on success |
|||
*/ |
|||
int (*init)(candev_t *dev); |
|||
|
|||
/**
|
|||
* @brief a driver's user-space ISR handler |
|||
* |
|||
* @param[in] dev CAN device descriptor |
|||
*/ |
|||
void (*isr)(candev_t *dev); |
|||
|
|||
/**
|
|||
* @brief Get an option value from a given CAN device |
|||
* |
|||
* @param[in] dev CAN device descriptor |
|||
* @param[in] opt option type |
|||
* @param[out] value pointer to store the option's value in |
|||
* @param[in] max_len maximal amount of byte that fit into @p value |
|||
* |
|||
* @return number of bytes written to @p value |
|||
* @return <0 on error |
|||
*/ |
|||
int (*get)(candev_t *dev, canopt_t opt, void *value, size_t max_len); |
|||
|
|||
/**
|
|||
* @brief Set an option value for a given CAN device |
|||
* |
|||
* @param[in] dev CAN device descriptor |
|||
* @param[in] opt option type |
|||
* @param[in] value value to set |
|||
* @param[in] value_len the length of @p value |
|||
* |
|||
* @return number of bytes used from @p value |
|||
* @return <0 on error |
|||
*/ |
|||
int (*set)(candev_t *dev, canopt_t opt, void *value, size_t value_len); |
|||
|
|||
/**
|
|||
* @brief Set a receive @p filter |
|||
* |
|||
* @param[in] dev CAN device descriptor |
|||
* @param[in] filter filter to set |
|||
* |
|||
* @return a positive filter number |
|||
* @return <0 on error |
|||
*/ |
|||
int (*set_filter)(candev_t *dev, const struct can_filter *filter); |
|||
|
|||
/**
|
|||
* @brief Remove a @p filter |
|||
* |
|||
* @param[in] dev CAN device descriptor |
|||
* @param[in] filter filter to remove |
|||
* |
|||
* @return 0 on success |
|||
* @return <0 on error |
|||
*/ |
|||
int (*remove_filter)(candev_t *dev, const struct can_filter *filter); |
|||
} candev_driver_t; |
|||
|
|||
#ifdef __cplusplus |
|||
} |
|||
#endif |
|||
|
|||
#endif /* CAN_CANDEV_H */ |
|||
/** @} */ |
@ -0,0 +1,3 @@ |
|||
MODULE = auto_init_can |
|||
|
|||
include $(RIOTBASE)/Makefile.base |
@ -0,0 +1,56 @@ |
|||
/*
|
|||
* 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 <toon.stegen@altran.com> |
|||
* @author Vincent Dupont <vincent@otakeys.com> |
|||
* @author Aurelien Gonce <aurelien.gonce@altran.com> |
|||
* @} |
|||
*/ |
|||
|
|||
#include <stdio.h> |
|||
|
|||
#define ENABLE_DEBUG (0) |
|||
#include "debug.h" |
|||
|
|||
#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(); |
|||
#endif |
|||
} |
@ -0,0 +1,57 @@ |
|||
/*
|
|||
* 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 <vincent@otakeys.com> |
|||
* @} |
|||
*/ |
|||
|
|||
#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_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; |
|||
#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 |
@ -0,0 +1,10 @@ |
|||
|
|||
ifneq (,$(filter can_isotp,$(USEMODULE))) |
|||
DIRS += isotp |
|||
endif |
|||
|
|||
ifneq (,$(filter conn_can,$(USEMODULE))) |
|||
DIRS += conn |
|||
endif |
|||
|
|||
include $(RIOTBASE)/Makefile.base |
@ -0,0 +1,3 @@ |
|||
MODULE = conn_can |
|||
|
|||
include $(RIOTBASE)/Makefile.base |
@ -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 |
|||