net: l2: 6LoCAN implementation
This commit is an implementation of 6LoCAN, a 6Lo adaption layer for Controller Area Networks. 6LoCAN is not yet standardised. Signed-off-by: Alexander Wachter <alexander.wachter@student.tugraz.at>
This commit is contained in:
parent
c8c5f3bbf3
commit
35f01673ac
|
@ -353,6 +353,7 @@
|
|||
/subsys/net/lib/sockets/ @jukkar @tbursztyka @pfalcon
|
||||
/subsys/net/lib/tls_credentials/ @rlubos
|
||||
/subsys/net/l2/ @jukkar @tbursztyka
|
||||
/subsys/net/l2/canbus/ @alexanderwachter @jukkar
|
||||
/subsys/power/ @wentongwu @pizi-nordic
|
||||
/subsys/settings/ @nvlsianpu
|
||||
/subsys/shell/ @jakub-uC @nordic-krch
|
||||
|
|
|
@ -8,3 +8,4 @@ zephyr_sources_ifdef(CONFIG_CAN_MCUX_FLEXCAN can_mcux_flexcan.c)
|
|||
|
||||
zephyr_sources_ifdef(CONFIG_USERSPACE can_handlers.c)
|
||||
zephyr_sources_ifdef(CONFIG_CAN_SHELL can_shell.c)
|
||||
zephyr_sources_ifdef(CONFIG_CAN_NET can_net.c)
|
||||
|
|
|
@ -64,5 +64,6 @@ source "drivers/can/Kconfig.stm32"
|
|||
source "drivers/can/Kconfig.mcux"
|
||||
source "drivers/can/Kconfig.mcp2515"
|
||||
source "drivers/can/Kconfig.loopback"
|
||||
source "drivers/can/Kconfig.net"
|
||||
|
||||
endif # CAN
|
||||
|
|
37
drivers/can/Kconfig.net
Normal file
37
drivers/can/Kconfig.net
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Kconfig.net - Configuration options for IPv6 over CAN
|
||||
|
||||
#
|
||||
# Copyright (c) 2019 Alexander Wachter
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
config CAN_NET
|
||||
bool "Enable 6loCAN network interface [EXPERIMENTAL]"
|
||||
help
|
||||
Enable IPv6 Networking over can (6loCAN)
|
||||
|
||||
if CAN_NET
|
||||
|
||||
module = CAN_NET
|
||||
module-dep = NET_LOG
|
||||
module-str = Log level for Network CAN
|
||||
module-help = Enables logging for CAN L2 networking
|
||||
source "subsys/net/Kconfig.template.log_config.net"
|
||||
|
||||
config CAN_NET_NAME
|
||||
string "Network device name"
|
||||
default "NET_CAN"
|
||||
help
|
||||
Name of the network device driver for IPv6 over CAN.
|
||||
|
||||
config CAN_NET_INIT_PRIORITY
|
||||
int "CAN NET driver init priority"
|
||||
default 80
|
||||
help
|
||||
CAN NET device driver initialization priority.
|
||||
Do not mess with it unless you know what you are doing.
|
||||
Note that the priority needs to be lower than the net stack
|
||||
so that it can start before the networking sub-system.
|
||||
|
||||
endif #CAN_NET
|
258
drivers/can/can_net.c
Normal file
258
drivers/can/can_net.c
Normal file
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Alexander Wachter
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <net/can.h>
|
||||
#include <net/net_pkt.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(net_can, CONFIG_CAN_NET_LOG_LEVEL);
|
||||
|
||||
struct net_can_context {
|
||||
struct device *can_dev;
|
||||
struct net_if *iface;
|
||||
int recv_filter_id;
|
||||
int mcast_filter_id;
|
||||
};
|
||||
|
||||
static inline u8_t can_get_frame_datalength(struct zcan_frame *frame)
|
||||
{
|
||||
/* TODO: Needs update when CAN FD support is added */
|
||||
return frame->dlc;
|
||||
}
|
||||
|
||||
static inline u16_t can_get_lladdr_src(struct zcan_frame *frame)
|
||||
{
|
||||
return (frame->ext_id >> CAN_NET_IF_ADDR_SRC_POS) &
|
||||
CAN_NET_IF_ADDR_MASK;
|
||||
}
|
||||
|
||||
static inline u16_t can_get_lladdr_dest(struct zcan_frame *frame)
|
||||
{
|
||||
return (frame->ext_id >> CAN_NET_IF_ADDR_DEST_POS) &
|
||||
CAN_NET_IF_ADDR_MASK;
|
||||
}
|
||||
|
||||
static inline void can_set_lladdr(struct net_pkt *pkt, struct zcan_frame *frame)
|
||||
{
|
||||
struct net_buf *buf = pkt->buffer;
|
||||
|
||||
/* Put the destination at the beginning of the pkt.
|
||||
* The net_canbus_lladdr has a size if 14 bits. To convert it to
|
||||
* network byte order, we treat it as 16 bits here.
|
||||
*/
|
||||
net_pkt_lladdr_dst(pkt)->addr = buf->data;
|
||||
net_pkt_lladdr_dst(pkt)->len = sizeof(struct net_canbus_lladdr);
|
||||
net_pkt_lladdr_dst(pkt)->type = NET_LINK_CANBUS;
|
||||
net_buf_add_be16(buf, can_get_lladdr_dest(frame));
|
||||
net_buf_pull(buf, sizeof(u16_t));
|
||||
|
||||
/* Do the same as above for the source address */
|
||||
net_pkt_lladdr_src(pkt)->addr = buf->data;
|
||||
net_pkt_lladdr_src(pkt)->len = sizeof(struct net_canbus_lladdr);
|
||||
net_pkt_lladdr_src(pkt)->type = NET_LINK_CANBUS;
|
||||
net_buf_add_be16(buf, can_get_lladdr_src(frame));
|
||||
net_buf_pull(buf, sizeof(u16_t));
|
||||
}
|
||||
|
||||
static void net_can_iface_init(struct net_if *iface)
|
||||
{
|
||||
struct device *dev = net_if_get_device(iface);
|
||||
struct net_can_context *ctx = dev->driver_data;
|
||||
|
||||
ctx->iface = iface;
|
||||
|
||||
NET_DBG("Init CAN network interface %p dev %p", iface, dev);
|
||||
|
||||
net_6locan_init(iface);
|
||||
}
|
||||
|
||||
static int net_can_send(struct device *dev, const struct zcan_frame *frame,
|
||||
can_tx_callback_t cb, void *cb_arg, s32_t timeout)
|
||||
{
|
||||
struct net_can_context *ctx = dev->driver_data;
|
||||
|
||||
NET_ASSERT(frame->id_type == CAN_EXTENDED_IDENTIFIER);
|
||||
return can_send(ctx->can_dev, frame, timeout, cb, cb_arg);
|
||||
}
|
||||
|
||||
static void net_can_recv(struct zcan_frame *frame, void *arg)
|
||||
{
|
||||
struct net_can_context *ctx = (struct net_can_context *)arg;
|
||||
size_t pkt_size = 2 * sizeof(struct net_canbus_lladdr) +
|
||||
can_get_frame_datalength(frame);
|
||||
struct net_pkt *pkt;
|
||||
int ret;
|
||||
|
||||
NET_DBG("Frame with ID 0x%x received", frame->ext_id);
|
||||
pkt = net_pkt_rx_alloc_with_buffer(ctx->iface, pkt_size, AF_UNSPEC, 0,
|
||||
K_NO_WAIT);
|
||||
if (!pkt) {
|
||||
LOG_ERR("Failed to obtain net_pkt with size of %d", pkt_size);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
pkt->canbus_rx_ctx = NULL;
|
||||
|
||||
can_set_lladdr(pkt, frame);
|
||||
net_pkt_cursor_init(pkt);
|
||||
ret = net_pkt_write(pkt, frame->data, can_get_frame_datalength(frame));
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to append frame data to net_pkt");
|
||||
goto drop;
|
||||
}
|
||||
|
||||
ret = net_recv_data(ctx->iface, pkt);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Packet dropped by NET stack");
|
||||
goto drop;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
drop:
|
||||
NET_INFO("pkt dropped");
|
||||
|
||||
if (pkt) {
|
||||
net_pkt_unref(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
static int can_attach_filter(struct device *dev, can_rx_callback_t cb, void *cb_arg,
|
||||
const struct zcan_filter *filter)
|
||||
{
|
||||
struct net_can_context *ctx = dev->driver_data;
|
||||
|
||||
return can_attach_isr(ctx->can_dev, cb, cb_arg, filter);
|
||||
}
|
||||
|
||||
static void can_detach_filter(struct device *dev, int filter_id)
|
||||
{
|
||||
struct net_can_context *ctx = dev->driver_data;
|
||||
|
||||
if (filter_id >= 0) {
|
||||
can_detach(ctx->can_dev, filter_id);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int can_attach_unicast_filter(struct net_can_context *ctx)
|
||||
{
|
||||
struct zcan_filter filter = {
|
||||
.id_type = CAN_EXTENDED_IDENTIFIER,
|
||||
.rtr = CAN_DATAFRAME,
|
||||
.rtr_mask = 1,
|
||||
.ext_id_mask = CAN_NET_IF_ADDR_DEST_MASK
|
||||
};
|
||||
const u8_t *link_addr = net_if_get_link_addr(ctx->iface)->addr;
|
||||
const u16_t dest = sys_be16_to_cpu(UNALIGNED_GET((u16_t *) link_addr));
|
||||
int filter_id;
|
||||
|
||||
filter.ext_id = (dest << CAN_NET_IF_ADDR_DEST_POS);
|
||||
|
||||
filter_id = can_attach_isr(ctx->can_dev, net_can_recv,
|
||||
ctx, &filter);
|
||||
if (filter_id == CAN_NET_FILTER_NOT_SET) {
|
||||
NET_ERR("Can't attach FF filter");
|
||||
return CAN_NET_FILTER_NOT_SET;
|
||||
}
|
||||
|
||||
NET_DBG("Attached FF filter %d", filter_id);
|
||||
|
||||
return filter_id;
|
||||
}
|
||||
|
||||
static inline int can_attach_mcast_filter(struct net_can_context *ctx)
|
||||
{
|
||||
struct zcan_filter filter = {
|
||||
.id_type = CAN_EXTENDED_IDENTIFIER,
|
||||
.rtr = CAN_DATAFRAME,
|
||||
.rtr_mask = 1,
|
||||
.ext_id_mask = CAN_NET_IF_ADDR_DEST_MASK
|
||||
};
|
||||
int filter_id;
|
||||
|
||||
filter.ext_id = (NET_CAN_MULTICAST_ADDR << CAN_NET_IF_ADDR_DEST_POS);
|
||||
|
||||
filter_id = can_attach_isr(ctx->can_dev, net_can_recv,
|
||||
ctx, &filter);
|
||||
if (filter_id == CAN_NET_FILTER_NOT_SET) {
|
||||
NET_ERR("Can't attach multicast filter");
|
||||
return CAN_NET_FILTER_NOT_SET;
|
||||
}
|
||||
|
||||
NET_DBG("Attached multicast filter %d", filter_id);
|
||||
|
||||
return filter_id;
|
||||
}
|
||||
|
||||
static int can_enable(struct device *dev, bool enable)
|
||||
{
|
||||
struct net_can_context *ctx = dev->driver_data;
|
||||
|
||||
if (enable) {
|
||||
if (ctx->recv_filter_id == CAN_NET_FILTER_NOT_SET) {
|
||||
ctx->recv_filter_id = can_attach_unicast_filter(ctx);
|
||||
if (ctx->recv_filter_id < 0) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->mcast_filter_id == CAN_NET_FILTER_NOT_SET) {
|
||||
ctx->mcast_filter_id = can_attach_mcast_filter(ctx);
|
||||
if (ctx->mcast_filter_id < 0) {
|
||||
can_detach(ctx->can_dev, ctx->recv_filter_id);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ctx->recv_filter_id != CAN_NET_FILTER_NOT_SET) {
|
||||
can_detach(ctx->can_dev, ctx->recv_filter_id);
|
||||
}
|
||||
|
||||
if (ctx->mcast_filter_id != CAN_NET_FILTER_NOT_SET) {
|
||||
can_detach(ctx->can_dev, ctx->mcast_filter_id);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct net_can_api net_can_api_inst = {
|
||||
.iface_api.init = net_can_iface_init,
|
||||
|
||||
.send = net_can_send,
|
||||
.attach_filter = can_attach_filter,
|
||||
.detach_filter = can_detach_filter,
|
||||
.enable = can_enable,
|
||||
};
|
||||
|
||||
static int net_can_init(struct device *dev)
|
||||
{
|
||||
struct device *can_dev = device_get_binding(DT_CAN_1_NAME);
|
||||
struct net_can_context *ctx = dev->driver_data;
|
||||
|
||||
ctx->recv_filter_id = CAN_NET_FILTER_NOT_SET;
|
||||
ctx->mcast_filter_id = CAN_NET_FILTER_NOT_SET;
|
||||
|
||||
if (!can_dev) {
|
||||
NET_ERR("Can't get binding to CAN device %s", DT_CAN_1_NAME);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
NET_DBG("Init net CAN device %p (%s) for dev %p (%s)",
|
||||
dev, dev->config->name, can_dev, can_dev->config->name);
|
||||
|
||||
ctx->can_dev = can_dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct net_can_context net_can_context_1;
|
||||
|
||||
NET_DEVICE_INIT(net_can_1, CONFIG_CAN_NET_NAME, net_can_init,
|
||||
&net_can_context_1, NULL,
|
||||
CONFIG_CAN_NET_INIT_PRIORITY,
|
||||
&net_can_api_inst,
|
||||
CANBUS_L2, NET_L2_GET_CTX_TYPE(CANBUS_L2), NET_CAN_MTU);
|
230
include/net/can.h
Normal file
230
include/net/can.h
Normal file
|
@ -0,0 +1,230 @@
|
|||
/** @file
|
||||
* @brief IPv6 Networking over CAN definitions.
|
||||
*
|
||||
* Definitions for IPv6 Networking over CAN support.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Alexander Wachter
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_NET_CAN_H_
|
||||
#define ZEPHYR_INCLUDE_NET_CAN_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <net/net_ip.h>
|
||||
#include <net/net_if.h>
|
||||
#include <can.h>
|
||||
|
||||
/**
|
||||
* @brief IPv6 over CAN library
|
||||
* @defgroup net_can Network Core Library
|
||||
* @ingroup networking
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* CAN L2 driver API. Used by 6loCAN.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Abbreviations
|
||||
* BS Block Size
|
||||
* CAN_DL CAN LL data size
|
||||
* CF Consecutive Frame
|
||||
* CTS Continue to send
|
||||
* DLC Data length code
|
||||
* FC Flow Control
|
||||
* FF First Frame
|
||||
* FS Flow Status
|
||||
*/
|
||||
|
||||
/** @cond INTERNAL_HIDDEN */
|
||||
|
||||
#define NET_CAN_DL 8
|
||||
#define NET_CAN_MTU 0x0FFF
|
||||
|
||||
/* 0x3DFF - bit 4 to 10 must not be zero. Also prevent stuffing bit*/
|
||||
#define NET_CAN_MULTICAST_ADDR 0x3DFF
|
||||
#define NET_CAN_DAD_ADDR 0x3DFE
|
||||
#define NET_CAN_ETH_TRANSLATOR_ADDR 0x3DF0
|
||||
#define NET_CAN_MAX_ADDR 0x3DEF
|
||||
#define NET_CAN_MIN_ADDR 0x0100
|
||||
|
||||
#define CAN_NET_IF_ADDR_MASK 0x3FFF
|
||||
#define CAN_NET_IF_ADDR_BYTE_LEN 2U
|
||||
#define CAN_NET_IF_ADDR_DEST_POS 14U
|
||||
#define CAN_NET_IF_ADDR_DEST_MASK (CAN_NET_IF_ADDR_MASK << CAN_NET_IF_ADDR_DEST_POS)
|
||||
#define CAN_NET_IF_ADDR_SRC_POS 0U
|
||||
#define CAN_NET_IF_ADDR_SRC_MASK (CAN_NET_IF_ADDR_MASK << CAN_NET_IF_ADDR_SRC_POS)
|
||||
#define CAN_NET_IF_ADDR_MCAST_POS 28U
|
||||
#define CAN_NET_IF_ADDR_MCAST_MASK (1UL << CAN_NET_IF_ADDR_MCAST_POS)
|
||||
|
||||
#define CAN_NET_IF_IS_MCAST_BIT (1U << 14)
|
||||
|
||||
#define CAN_NET_FILTER_NOT_SET -1
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/*
|
||||
* +-----------+ +-----------+
|
||||
* | | | |
|
||||
* | IPv6 | | IPv6 |
|
||||
* | 6LoCAN | | 6LoCAN |
|
||||
* | | | |
|
||||
* +----+-+----+ +----+-+----+
|
||||
* | | | | +---+
|
||||
* +----+ | | | | / \ +-+
|
||||
* | | | | | | +--+ / \_/ \
|
||||
* +++ +---+ +--+----+ +-------+ +--+----+ +--/ \_/ \
|
||||
* | | \ / | \ / \ / | \ / / |
|
||||
* | | X | X CAN X | X | Internet |
|
||||
* | | / \ | / \ / \ | / \ \ /
|
||||
* +++ +---+ +----+--+ +-------+ +----+--+ +--+ /
|
||||
* | | +-------------------+
|
||||
* +----+
|
||||
*/
|
||||
struct net_can_api {
|
||||
/**
|
||||
* The net_if_api must be placed in first position in this
|
||||
* struct so that we are compatible with network interface API.
|
||||
*/
|
||||
struct net_if_api iface_api;
|
||||
|
||||
/** Send a single CAN frame */
|
||||
int (*send)(struct device *dev, const struct zcan_frame *frame,
|
||||
can_tx_callback_t cb, void *cb_arg, s32_t timeout);
|
||||
/** Attach a filter with it's callback */
|
||||
int (*attach_filter)(struct device *dev, can_rx_callback_t cb,
|
||||
void *cb_arg, const struct zcan_filter *filter);
|
||||
/** Detach a filter */
|
||||
void (*detach_filter)(struct device *dev, int filter_id);
|
||||
/** Enable or disable the reception of frames for net CAN */
|
||||
int (*enable)(struct device *dev, bool enable);
|
||||
};
|
||||
|
||||
/** @cond INTERNAL_HIDDEN */
|
||||
|
||||
#define CANBUS_L2_CTX_TYPE struct net_canbus_context *
|
||||
|
||||
/**
|
||||
* Context for canbus net device.
|
||||
*/
|
||||
struct canbus_net_ctx {
|
||||
/** Filter ID for link layer duplicate address detection. */
|
||||
int dad_filter_id;
|
||||
/** Work item for responding to link layer DAD requests. */
|
||||
struct k_work dad_work;
|
||||
/** The interface associated with this device */
|
||||
struct net_if *iface;
|
||||
/** The link layer address chosen for this interface */
|
||||
u16_t ll_addr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Canbus link layer addresses have a length of 14 bit for source and destination.
|
||||
* Both together are 28 bit to fit a CAN extended identifier with 29 bit length.
|
||||
*/
|
||||
struct net_canbus_lladdr {
|
||||
u16_t addr : 14;
|
||||
};
|
||||
|
||||
/**
|
||||
* STmin is split in two valid ranges:
|
||||
* 0-127: 0ms-127ms
|
||||
* 128-240: Reserved
|
||||
* 241-249: 100us-900us (multiples of 100us)
|
||||
* 250- : Reserved
|
||||
*/
|
||||
struct canbus_fc_opts {
|
||||
/** Block size. Number of CF PDUs before next CF is sent */
|
||||
u8_t bs;
|
||||
/**< Minimum separation time. Min time between frames */
|
||||
u8_t stmin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Context for a transmission of messages that didn't fit in a single frame.
|
||||
* These messages Start with a FF (First Frame) that is in case of unicast
|
||||
* acknowledged by a FC (Frame Control). After that, one or more CF
|
||||
* (Consecutive frames) carry the rest of the message.
|
||||
*/
|
||||
struct canbus_isotp_tx_ctx {
|
||||
/** Pkt containing the data to transmit */
|
||||
struct net_pkt *pkt;
|
||||
/** Timeout for TX Timeout and separation time */
|
||||
struct _timeout timeout;
|
||||
/** Frame Control options received from FC frame */
|
||||
struct canbus_fc_opts opts;
|
||||
/** CAN destination address */
|
||||
struct net_canbus_lladdr dest_addr;
|
||||
/** Remaining data to transmit in bytes */
|
||||
u16_t rem_len;
|
||||
/** Number of bytes in the tx queue */
|
||||
s8_t tx_backlog;
|
||||
/** State of the transmission */
|
||||
u8_t state;
|
||||
/** Actual block number that is transmitted. Counts from BS to 0 */
|
||||
u8_t act_block_nr;
|
||||
/** Number of WAIT frames received */
|
||||
u8_t wft;
|
||||
/** Sequence number that is added to CF */
|
||||
u8_t sn : 4;
|
||||
/** Transmission is multicast */
|
||||
u8_t is_mcast : 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Context for reception of messages that are not single frames.
|
||||
* This is the counterpart of the canbus_isotp_tx_ctx.
|
||||
*/
|
||||
struct canbus_isotp_rx_ctx {
|
||||
/** Pkt that is large enough to hold the entire message */
|
||||
struct net_pkt *pkt;
|
||||
/** Timeout for RX timeout*/
|
||||
struct _timeout timeout;
|
||||
/** Remaining data to receive. Goes from message length to zero */
|
||||
u16_t rem_len;
|
||||
/** State of the reception */
|
||||
u8_t state;
|
||||
/** Number of frames received in this block. Counts from BS to 0 */
|
||||
u8_t act_block_nr;
|
||||
/** Number of WAIT frames transmitted */
|
||||
u8_t wft;
|
||||
/** Expected sequence number in CF */
|
||||
u8_t sn : 4;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialization of the canbus L2.
|
||||
*
|
||||
* This function starts the TX workqueue and does some initialization.
|
||||
*/
|
||||
void net_6locan_init(struct net_if *iface);
|
||||
|
||||
/**
|
||||
* Ethernet frame input function for Ethernet to 6LoCAN translation
|
||||
*
|
||||
* This function checks the destination link layer address for addresses
|
||||
* that has to be forwarded. Frames that need to be forwarded are forwarded here.
|
||||
*/
|
||||
enum net_verdict net_canbus_translate_eth_frame(struct net_if *iface,
|
||||
struct net_pkt *pkt);
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_NET_CAN_H_ */
|
|
@ -1038,7 +1038,8 @@ static inline void net_ipv6_addr_create_iid(struct in6_addr *addr,
|
|||
/* The generated IPv6 shall not toggle the
|
||||
* Universal/Local bit. RFC 6282 ch 3.2.2
|
||||
*/
|
||||
if (lladdr->type == NET_LINK_IEEE802154) {
|
||||
if (lladdr->type == NET_LINK_IEEE802154 ||
|
||||
lladdr->type == NET_LINK_CANBUS) {
|
||||
UNALIGNED_PUT(0, &addr->s6_addr32[2]);
|
||||
addr->s6_addr[11] = 0xff;
|
||||
addr->s6_addr[12] = 0xfe;
|
||||
|
|
|
@ -120,6 +120,11 @@ NET_L2_DECLARE_PUBLIC(OPENTHREAD_L2);
|
|||
NET_L2_DECLARE_PUBLIC(CANBUS_RAW_L2);
|
||||
#endif /* CONFIG_NET_L2_CANBUS_RAW */
|
||||
|
||||
#ifdef CONFIG_NET_L2_CANBUS
|
||||
#define CANBUS_L2 CANBUS
|
||||
NET_L2_DECLARE_PUBLIC(CANBUS_L2);
|
||||
#endif /* CONFIG_NET_L2_CANBUS */
|
||||
|
||||
#define NET_L2_INIT(_name, _recv_fn, _send_fn, _enable_fn, _get_flags_fn) \
|
||||
const struct net_l2 (NET_L2_GET_NAME(_name)) __used \
|
||||
__attribute__((__section__(".net_l2.init"))) = { \
|
||||
|
|
|
@ -57,6 +57,8 @@ enum net_link_type {
|
|||
NET_LINK_DUMMY,
|
||||
/** CANBUS link address. */
|
||||
NET_LINK_CANBUS_RAW,
|
||||
/** 6loCAN link address. */
|
||||
NET_LINK_CANBUS,
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
|
|
|
@ -41,6 +41,8 @@ extern "C" {
|
|||
*/
|
||||
|
||||
struct net_context;
|
||||
struct canbus_net_isotp_tx_ctx;
|
||||
struct canbus_net_isotp_rx_ctx;
|
||||
|
||||
|
||||
/* buffer cursor used in net_pkt */
|
||||
|
@ -208,6 +210,12 @@ struct net_pkt {
|
|||
#if defined(CONFIG_IEEE802154)
|
||||
u8_t ieee802154_rssi; /* Received Signal Strength Indication */
|
||||
u8_t ieee802154_lqi; /* Link Quality Indicator */
|
||||
#endif
|
||||
#if defined(CONFIG_NET_L2_CANBUS)
|
||||
union {
|
||||
struct canbus_isotp_tx_ctx *canbus_tx_ctx;
|
||||
struct canbus_isotp_rx_ctx *canbus_rx_ctx;
|
||||
};
|
||||
#endif
|
||||
/* @endcond */
|
||||
};
|
||||
|
|
|
@ -12,7 +12,7 @@ CONFIG_NET_MGMT=n
|
|||
CONFIG_NET_TCP=n
|
||||
CONFIG_NET_UDP=n
|
||||
|
||||
CONFIG_NET_DEFAULT_IF_CANBUS=y
|
||||
CONFIG_NET_DEFAULT_IF_CANBUS_RAW=y
|
||||
|
||||
CONFIG_NET_PKT_RX_COUNT=30
|
||||
CONFIG_NET_PKT_TX_COUNT=30
|
||||
|
|
|
@ -81,7 +81,7 @@ static int create_socket(const struct can_filter *filter)
|
|||
}
|
||||
|
||||
can_addr.can_ifindex = net_if_get_by_iface(
|
||||
net_if_get_first_by_type(&NET_L2_GET_NAME(CANBUS)));
|
||||
net_if_get_first_by_type(&NET_L2_GET_NAME(CANBUS_RAW)));
|
||||
can_addr.can_family = PF_CAN;
|
||||
|
||||
ret = bind(fd, (struct sockaddr *)&can_addr, sizeof(can_addr));
|
||||
|
@ -170,7 +170,7 @@ static int setup_socket(void)
|
|||
|
||||
can_copy_zfilter_to_filter(&zfilter, &filter);
|
||||
|
||||
iface = net_if_get_first_by_type(&NET_L2_GET_NAME(CANBUS));
|
||||
iface = net_if_get_first_by_type(&NET_L2_GET_NAME(CANBUS_RAW));
|
||||
if (!iface) {
|
||||
LOG_ERR("No CANBUS network interface found!");
|
||||
return -ENOENT;
|
||||
|
|
|
@ -547,9 +547,14 @@ config NET_DEFAULT_IF_DUMMY
|
|||
depends on NET_L2_DUMMY
|
||||
|
||||
config NET_DEFAULT_IF_CANBUS
|
||||
bool "Socket CAN interface"
|
||||
bool "6LoCAN (IPv6 over CAN) interface"
|
||||
depends on NET_L2_CANBUS
|
||||
|
||||
config NET_DEFAULT_IF_CANBUS_RAW
|
||||
bool "Socket CAN interface"
|
||||
depends on NET_L2_CANBUS_RAW
|
||||
|
||||
|
||||
endchoice
|
||||
|
||||
config NET_PKT_TIMESTAMP
|
||||
|
|
|
@ -21,7 +21,7 @@ LOG_MODULE_REGISTER(net_sockets_can, CONFIG_NET_SOCKETS_LOG_LEVEL);
|
|||
enum net_verdict net_canbus_socket_input(struct net_pkt *pkt)
|
||||
{
|
||||
if (net_pkt_family(pkt) == AF_CAN &&
|
||||
net_if_l2(net_pkt_iface(pkt)) == &NET_L2_GET_NAME(CANBUS)) {
|
||||
net_if_l2(net_pkt_iface(pkt)) == &NET_L2_GET_NAME(CANBUS_RAW)) {
|
||||
return net_conn_input(pkt, NULL, CAN_RAW, NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -976,6 +976,8 @@ static inline u8_t get_llao_len(struct net_if *iface)
|
|||
return 8;
|
||||
} else if (net_if_get_link_addr(iface)->len == 8U) {
|
||||
return 16;
|
||||
} else if (net_if_get_link_addr(iface)->len == 2U) {
|
||||
return 8;
|
||||
}
|
||||
|
||||
/* What else could it be? */
|
||||
|
|
|
@ -446,11 +446,11 @@ static int bind_default(struct net_context *context)
|
|||
if (context->iface >= 0) {
|
||||
return 0;
|
||||
} else {
|
||||
#if defined(CONFIG_NET_L2_CANBUS)
|
||||
#if defined(CONFIG_NET_L2_CANBUS_RAW)
|
||||
struct net_if *iface;
|
||||
|
||||
iface = net_if_get_first_by_type(
|
||||
&NET_L2_GET_NAME(CANBUS));
|
||||
&NET_L2_GET_NAME(CANBUS_RAW));
|
||||
if (!iface) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
|
|
@ -399,6 +399,9 @@ struct net_if *net_if_get_default(void)
|
|||
#if defined(CONFIG_NET_DEFAULT_IF_OFFLOAD)
|
||||
iface = net_if_get_first_by_type(NULL);
|
||||
#endif
|
||||
#if defined(CONFIG_NET_DEFAULT_IF_CANBUS_RAW)
|
||||
iface = net_if_get_first_by_type(&NET_L2_GET_NAME(CANBUS_RAW));
|
||||
#endif
|
||||
#if defined(CONFIG_NET_DEFAULT_IF_CANBUS)
|
||||
iface = net_if_get_first_by_type(&NET_L2_GET_NAME(CANBUS));
|
||||
#endif
|
||||
|
|
|
@ -207,6 +207,16 @@ static const char *iface2str(struct net_if *iface, const char **extra)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NET_L2_CANBUS_RAW
|
||||
if (net_if_l2(iface) == &NET_L2_GET_NAME(CANBUS_RAW)) {
|
||||
if (extra) {
|
||||
*extra = "==========";
|
||||
}
|
||||
|
||||
return "CANBUS_RAW";
|
||||
}
|
||||
#endif
|
||||
|
||||
if (extra) {
|
||||
*extra = "==============";
|
||||
}
|
||||
|
|
|
@ -172,7 +172,9 @@ static inline u32_t retry_timeout(const struct net_tcp *tcp)
|
|||
((IS_ENABLED(CONFIG_NET_L2_BT) && \
|
||||
net_pkt_lladdr_dst(pkt)->type == NET_LINK_BLUETOOTH) || \
|
||||
(IS_ENABLED(CONFIG_NET_L2_IEEE802154) && \
|
||||
net_pkt_lladdr_dst(pkt)->type == NET_LINK_IEEE802154)))
|
||||
net_pkt_lladdr_dst(pkt)->type == NET_LINK_IEEE802154) || \
|
||||
(IS_ENABLED(CONFIG_NET_L2_CANBUS) && \
|
||||
net_pkt_lladdr_dst(pkt)->type == NET_LINK_CANBUS)))
|
||||
|
||||
/* The ref should not be done for Bluetooth and IEEE 802.15.4 which use
|
||||
* IPv6 header compression (6lo). For BT and 802.15.4 we copy the pkt
|
||||
|
|
|
@ -95,6 +95,9 @@ char *net_sprint_ll_addr_buf(const u8_t *ll, u8_t ll_len,
|
|||
case 6:
|
||||
len = 6U;
|
||||
break;
|
||||
case 2:
|
||||
len = 2U;
|
||||
break;
|
||||
default:
|
||||
len = 6U;
|
||||
break;
|
||||
|
|
|
@ -28,6 +28,6 @@ if(CONFIG_NET_L2_WIFI_MGMT OR CONFIG_NET_L2_WIFI_SHELL)
|
|||
add_subdirectory(wifi)
|
||||
endif()
|
||||
|
||||
if(CONFIG_NET_L2_CANBUS)
|
||||
if(CONFIG_NET_L2_CANBUS OR CONFIG_NET_L2_CANBUS_RAW)
|
||||
add_subdirectory(canbus)
|
||||
endif()
|
||||
|
|
|
@ -77,6 +77,8 @@ source "subsys/net/l2/ieee802154/Kconfig"
|
|||
|
||||
source "subsys/net/l2/openthread/Kconfig"
|
||||
|
||||
source "subsys/net/l2/canbus/Kconfig"
|
||||
|
||||
config NET_L2_WIFI_MGMT
|
||||
bool "Enable Wi-Fi Management support"
|
||||
select NET_MGMT
|
||||
|
@ -100,9 +102,4 @@ config NET_L2_WIFI_SHELL
|
|||
This can be used for controlling Wi-Fi through the console via
|
||||
exposing a shell module named "wifi".
|
||||
|
||||
config NET_L2_CANBUS
|
||||
bool "Enable CANBUS l2 layer"
|
||||
help
|
||||
Add a CANBUS L2 layer driver.
|
||||
|
||||
endmenu
|
||||
|
|
1344
subsys/net/l2/canbus/6locan.c
Normal file
1344
subsys/net/l2/canbus/6locan.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,10 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
zephyr_library_include_directories(. ${ZEPHYR_BASE}/subsys/net/ip)
|
||||
zephyr_library_compile_definitions_ifdef(
|
||||
CONFIG_NEWLIB_LIBC __LINUX_ERRNO_EXTENSIONS__
|
||||
)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_NET_L2_CANBUS canbus.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_NET_L2_CANBUS_RAW canbus_raw.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_NET_L2_CANBUS 6locan.c)
|
||||
|
|
68
subsys/net/l2/canbus/Kconfig
Normal file
68
subsys/net/l2/canbus/Kconfig
Normal file
|
@ -0,0 +1,68 @@
|
|||
#
|
||||
# Copyright (c) 2019 Alexander Wachter
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
config NET_L2_CANBUS
|
||||
bool "Enable CANBUS L2 layer [EXPERIMENTAL]"
|
||||
depends on CAN_NET
|
||||
select NET_6LO
|
||||
help
|
||||
Add a CANBUS L2 layer driver. This is the layer for IPv6 over CAN
|
||||
(6loCAN). It uses IPHC to compress the IP header and ISO-TP for
|
||||
flow control and reassembling.
|
||||
|
||||
if NET_L2_CANBUS
|
||||
|
||||
config NET_L2_CANBUS_USE_FIXED_ADDR
|
||||
bool "Use fixed L2 address"
|
||||
help
|
||||
Use a fixed L2 address for 6LoCAN instead of a random chosen one.
|
||||
|
||||
config NET_L2_CANBUS_FIXED_ADDR
|
||||
hex "L2 address"
|
||||
depends on NET_L2_CANBUS_USE_FIXED_ADDR
|
||||
range 0x00FF 0x3DEF
|
||||
|
||||
config NET_L2_CANBUS_DAD_RETRIES
|
||||
int "Number of DAD retries"
|
||||
default 5
|
||||
help
|
||||
Number of retries for Duplicate Address Detection.
|
||||
Greater than one only makes sense for random link layer addresses.
|
||||
|
||||
config NET_L2_CANBUS_STMIN
|
||||
int "STmin"
|
||||
default 0
|
||||
range 0 127
|
||||
help
|
||||
Minimal separation time between frames in ms.
|
||||
The timer starts when the frame is queued and the next frame is
|
||||
transmitted after expiration.
|
||||
STmin is chosen by the receiver and transmitted in the FC
|
||||
(Flow Control) frame. See also: ISO 15765-2:2016
|
||||
|
||||
config NET_L2_CANBUS_BS
|
||||
int "BS (Block Size)"
|
||||
default 8
|
||||
range 0 256
|
||||
help
|
||||
Number of CF (Contiguous Frame) PDUs before next FC (Flow Control)
|
||||
frame is sent. Zero value means all frames are sent consecutive
|
||||
without an additional FC frame.
|
||||
A BS counter at the sender counts from one to BS. When BS is reached,
|
||||
the sender waits for a FC frame again an BS is reset.
|
||||
See also: ISO 15765-2:2016
|
||||
|
||||
module = NET_L2_CANBUS
|
||||
module-dep = NET_LOG
|
||||
module-str = Log level for CANbus L2 layer
|
||||
module-help = Enables CANbus L2 to output debug messages.
|
||||
source "subsys/net/Kconfig.template.log_config.net"
|
||||
endif #NET_L2_CANBUS
|
||||
|
||||
config NET_L2_CANBUS_RAW
|
||||
bool "Enable CANBUS RAW l2 layer"
|
||||
help
|
||||
Add a CANBUS L2 layer driver. This is the layer for SOCKET CAN.
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(net_l2_canbus, LOG_LEVEL_NONE);
|
||||
|
||||
#include <net/net_core.h>
|
||||
#include <net/net_l2.h>
|
||||
#include <net/net_if.h>
|
||||
#include <net/net_pkt.h>
|
||||
#include <net/socket_can.h>
|
||||
|
||||
static inline enum net_verdict canbus_recv(struct net_if *iface,
|
||||
struct net_pkt *pkt)
|
||||
{
|
||||
net_pkt_lladdr_src(pkt)->addr = NULL;
|
||||
net_pkt_lladdr_src(pkt)->len = 0U;
|
||||
net_pkt_lladdr_src(pkt)->type = NET_LINK_CANBUS;
|
||||
net_pkt_lladdr_dst(pkt)->addr = NULL;
|
||||
net_pkt_lladdr_dst(pkt)->len = 0U;
|
||||
net_pkt_lladdr_dst(pkt)->type = NET_LINK_CANBUS;
|
||||
|
||||
net_pkt_set_family(pkt, AF_CAN);
|
||||
|
||||
return NET_CONTINUE;
|
||||
}
|
||||
|
||||
static inline int canbus_send(struct net_if *iface, struct net_pkt *pkt)
|
||||
{
|
||||
const struct canbus_api *api = net_if_get_device(iface)->driver_api;
|
||||
int ret;
|
||||
|
||||
if (!api) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ret = api->send(net_if_get_device(iface), pkt);
|
||||
if (!ret) {
|
||||
ret = net_pkt_get_len(pkt);
|
||||
net_pkt_unref(pkt);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NET_L2_INIT(CANBUS_L2, canbus_recv, canbus_send, NULL, NULL);
|
98
subsys/net/l2/canbus/canbus_internal.h
Normal file
98
subsys/net/l2/canbus/canbus_internal.h
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Alexander Wachter
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_SUBSYS_NET_L2_CANBUS_INTERNAL_H_
|
||||
#define ZEPHYR_SUBSYS_NET_L2_CANBUS_INTERNAL_H_
|
||||
|
||||
|
||||
#ifdef NET_CAN_USE_CAN_FD
|
||||
#define NET_CAN_DL 64
|
||||
#else
|
||||
#define NET_CAN_DL 8
|
||||
#endif/*NET_CAN_USE_CAN_FD*/
|
||||
|
||||
/* Protocol control information*/
|
||||
#define NET_CAN_PCI_SF 0x00 /* Single frame*/
|
||||
#define NET_CAN_PCI_FF 0x01 /* First frame */
|
||||
#define NET_CAN_PCI_CF 0x02 /* Consecutive frame */
|
||||
#define NET_CAN_PCI_FC 0x03 /* Flow control frame */
|
||||
|
||||
#define NET_CAN_PCI_TYPE_BYTE 0
|
||||
#define NET_CAN_PCI_TYPE_POS 4
|
||||
#define NET_CAN_PCI_TYPE_MASK 0xF0
|
||||
#define NET_CAN_PCI_TYPE_SF (NET_CAN_PCI_SF << NET_CAN_PCI_TYPE_POS)
|
||||
#define NET_CAN_PCI_TYPE_FF (NET_CAN_PCI_FF << NET_CAN_PCI_TYPE_POS)
|
||||
#define NET_CAN_PCI_TYPE_CF (NET_CAN_PCI_CF << NET_CAN_PCI_TYPE_POS)
|
||||
#define NET_CAN_PCI_TYPE_FC (NET_CAN_PCI_FC << NET_CAN_PCI_TYPE_POS)
|
||||
|
||||
#define NET_CAN_PCI_SF_DL_MASK 0x0F
|
||||
|
||||
#define NET_CAN_PCI_FF_DL_UPPER_BYTE 0
|
||||
#define NET_CAN_PCI_FF_DL_UPPER_MASK 0x0F
|
||||
#define NET_CAN_PCI_FF_DL_LOWER_BYTE 1
|
||||
|
||||
#define NET_CAN_PCI_FS_BYTE 0
|
||||
#define NET_CAN_PCI_FS_MASK 0x0F
|
||||
#define NET_CAN_PCI_BS_BYTE 1
|
||||
#define NET_CAN_PCI_ST_MIN_BYTE 2
|
||||
|
||||
#define NET_CAN_PCI_FS_CTS 0x0
|
||||
#define NET_CAN_PCI_FS_WAIT 0x1
|
||||
#define NET_CAN_PCI_FS_OVFLW 0x2
|
||||
|
||||
#define NET_CAN_PCI_SN_MASK 0x0F
|
||||
|
||||
#define NET_CAN_FF_DL_MIN (NET_CAN_CAN_DL)
|
||||
|
||||
#define NET_CAN_WFT_FIRST 0xFF
|
||||
|
||||
#define NET_CAN_BS_TIME K_MSEC(1000)
|
||||
#define NET_CAN_A_TIME K_MSEC(1000)
|
||||
|
||||
#define NET_CAN_FF_CF_TIME K_MSEC(1)
|
||||
|
||||
#define NET_CAN_STMIN_MAX 0xFA
|
||||
#define NET_CAN_STMIN_MS_MAX 0x7F
|
||||
#define NET_CAN_STMIN_US_BEGIN 0xF1
|
||||
#define NET_CAN_STMIN_US_END 0xF9
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum net_can_isotp_tx_state {
|
||||
NET_CAN_TX_STATE_UNUSED,
|
||||
NET_CAN_TX_STATE_RESET,
|
||||
NET_CAN_TX_STATE_WAIT_FC,
|
||||
NET_CAN_TX_STATE_SEND_CF,
|
||||
NET_CAN_TX_STATE_WAIT_ST,
|
||||
NET_CAN_TX_STATE_WAIT_TX_BACKLOG,
|
||||
NET_CAN_TX_STATE_FIN,
|
||||
NET_CAN_TX_STATE_ERR
|
||||
};
|
||||
|
||||
enum net_can_isotp_rx_state {
|
||||
NET_CAN_RX_STATE_UNUSED,
|
||||
NET_CAN_RX_STATE_RESET,
|
||||
NET_CAN_RX_STATE_FF,
|
||||
NET_CAN_RX_STATE_CF,
|
||||
NET_CAN_RX_STATE_FIN,
|
||||
NET_CAN_RX_STATE_TIMEOUT
|
||||
};
|
||||
|
||||
struct canbus_l2_ctx {
|
||||
struct canbus_isotp_tx_ctx tx_ctx[CONFIG_NET_PKT_TX_COUNT];
|
||||
struct canbus_isotp_rx_ctx rx_ctx[CONFIG_NET_PKT_RX_COUNT];
|
||||
struct k_mutex tx_ctx_mtx;
|
||||
struct k_mutex rx_ctx_mtx;
|
||||
struct k_sem tx_sem;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_SUBSYS_NET_L2_CANBUS_INTERNAL_H_ */
|
|
@ -95,6 +95,7 @@ CONFIG_NET_L2_IEEE802154_SECURITY=y
|
|||
CONFIG_NET_L2_IEEE802154_SECURITY_CRYPTO_DEV_NAME="CRYPTO-DEV"
|
||||
CONFIG_NET_L2_DUMMY=y
|
||||
CONFIG_NET_L2_ETHERNET=y
|
||||
CONFIG_NET_L2_CANBUS=y
|
||||
CONFIG_NET_L2_CANBUS_RAW=y
|
||||
CONFIG_NET_L2_ETHERNET_MGMT=y
|
||||
CONFIG_NET_L2_IEEE802154_RADIO_DFLT_TX_POWER=2
|
||||
|
@ -368,6 +369,7 @@ CONFIG_NET_SOCKETS_TLS_MAX_CREDENTIALS=10
|
|||
|
||||
# Network interface defaults
|
||||
CONFIG_NET_DEFAULT_IF_BLUETOOTH=y
|
||||
CONFIG_NET_DEFAULT_IF_CANBUS=y
|
||||
CONFIG_NET_DEFAULT_IF_CANBUS_RAW=y
|
||||
CONFIG_NET_DEFAULT_IF_DUMMY=y
|
||||
CONFIG_NET_DEFAULT_IF_ETHERNET=y
|
||||
|
|
Loading…
Reference in a new issue