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
28 changed files with 2096 additions and 65 deletions
|
@ -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);
|
Loading…
Add table
Add a link
Reference in a new issue