From 202f7428ff96b2e4c9707fc7a8175d30567dd730 Mon Sep 17 00:00:00 2001 From: Sebastian Schildt Date: Fri, 27 Dec 2024 16:15:59 +0100 Subject: [PATCH] A kernel module wrapper fro ACF-CAN This is a Linux kernel driver that provides a virtual CAN interface for sending IEEE-1722 ACF-CAN frames directly. Signed-off-by: Sebastian Schildt --- .gitignore | 3 +- examples/acf-can/linux-kernel-mod/.gitignore | 73 ++++ .../acf-can/linux-kernel-mod/1722ethernet.c | 328 ++++++++++++++++++ .../acf-can/linux-kernel-mod/1722ethernet.h | 27 ++ examples/acf-can/linux-kernel-mod/Makefile | 11 + examples/acf-can/linux-kernel-mod/Readme.md | 134 +++++++ examples/acf-can/linux-kernel-mod/acfcandev.h | 67 ++++ .../acf-can/linux-kernel-mod/acfcandevsysfs.h | 243 +++++++++++++ .../acf-can/linux-kernel-mod/acfcankernel.svg | 4 + .../acf-can/linux-kernel-mod/acfcanmain.c | 319 +++++++++++++++++ .../linux-kernel-mod/acfcanmodulemetadata.h | 17 + .../acf-can/linux-kernel-mod/installmon.sh | 3 + examples/acf-can/linux-kernel-mod/myup.sh | 28 ++ examples/acf-can/linux-kernel-mod/up.sh | 4 + examples/acf-can/linux-kernel-mod/upfd.sh | 4 + include/avtp/Byteorder.h | 4 + include/avtp/CommonHeader.h | 2 - include/avtp/Defines.h | 5 +- include/avtp/Utils.h | 1 - include/avtp/acf/AcfCommon.h | 1 - include/avtp/acf/Can.h | 3 +- include/avtp/acf/Ntscf.h | 6 +- include/avtp/acf/Tscf.h | 1 - src/avtp/CommonHeader.c | 9 +- src/avtp/Utils.c | 10 +- src/avtp/acf/Can.c | 6 +- src/avtp/acf/Ntscf.c | 9 +- src/avtp/acf/Tscf.c | 8 +- 28 files changed, 1311 insertions(+), 19 deletions(-) create mode 100644 examples/acf-can/linux-kernel-mod/.gitignore create mode 100644 examples/acf-can/linux-kernel-mod/1722ethernet.c create mode 100644 examples/acf-can/linux-kernel-mod/1722ethernet.h create mode 100644 examples/acf-can/linux-kernel-mod/Makefile create mode 100644 examples/acf-can/linux-kernel-mod/Readme.md create mode 100644 examples/acf-can/linux-kernel-mod/acfcandev.h create mode 100644 examples/acf-can/linux-kernel-mod/acfcandevsysfs.h create mode 100644 examples/acf-can/linux-kernel-mod/acfcankernel.svg create mode 100644 examples/acf-can/linux-kernel-mod/acfcanmain.c create mode 100644 examples/acf-can/linux-kernel-mod/acfcanmodulemetadata.h create mode 100755 examples/acf-can/linux-kernel-mod/installmon.sh create mode 100755 examples/acf-can/linux-kernel-mod/myup.sh create mode 100755 examples/acf-can/linux-kernel-mod/up.sh create mode 100755 examples/acf-can/linux-kernel-mod/upfd.sh diff --git a/.gitignore b/.gitignore index 94fe448..42996c1 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ /CPackSourceConfig.cmake /install_manifest.txt /_CPack_Packages - +*.o +*.o.cmd *~ .vscode diff --git a/examples/acf-can/linux-kernel-mod/.gitignore b/examples/acf-can/linux-kernel-mod/.gitignore new file mode 100644 index 0000000..c1402f5 --- /dev/null +++ b/examples/acf-can/linux-kernel-mod/.gitignore @@ -0,0 +1,73 @@ +# kernel excludes +# Normal rules +# +.* +*.o +*.o.* +*.a +*.s +*.ko +*.so +*.so.dbg +*.mod.c +*.mod +*.i +*.lst +*.symtypes +*.order +modules.builtin +*.elf +*.bin +*.gz +*.bz2 +*.lzma +*.xz +*.lzo +*.patch +*.gcno + +# +# Top-level generic files +# +/tags +/TAGS +/linux +/vmlinux +/vmlinuz +/System.map +/Module.markers +/Module.symvers + +# +# git files that we don't want to ignore even it they are dot-files +# +!.gitignore +!.mailmap + +# +# Generated include files +# +include/config +include/linux/version.h +include/generated +arch/*/include/generated + +# stgit generated dirs +patches-* + +# quilt's files +patches +series + +# cscope files +cscope.* +ncscope.* + +# gnu global files +GPATH +GRTAGS +GSYMS +GTAGS + +*.orig +*~ \ No newline at end of file diff --git a/examples/acf-can/linux-kernel-mod/1722ethernet.c b/examples/acf-can/linux-kernel-mod/1722ethernet.c new file mode 100644 index 0000000..8a407f4 --- /dev/null +++ b/examples/acf-can/linux-kernel-mod/1722ethernet.c @@ -0,0 +1,328 @@ + +#include "1722ethernet.h" + +#include +#include +#include +#include +#include +#include + +void prepare_ntscf_header(Avtp_Ntscf_t *ntscf_header, struct acfcan_cfg *cfg) +{ + Avtp_Ntscf_Init(ntscf_header); + Avtp_Ntscf_SetVersion(ntscf_header, 0); + Avtp_Ntscf_SetSequenceNum(ntscf_header, cfg->sequenceNum++); // This can't be right. Increase? + Avtp_Ntscf_SetStreamId(ntscf_header, cfg->tx_streamid); +} + +void prepare_can_header(Avtp_Can_t *can_header, struct acfcan_cfg *cfg, const struct sk_buff *skb) +{ + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; // this also works work can_frame struct, as the beginning is similar + + Avtp_Can_Init(can_header); + Avtp_Can_SetCanBusId(can_header, cfg->canbusId); + if (cfd->can_id & CAN_RTR_FLAG) + { + Avtp_Can_EnableRtr(can_header); + } + if (cfd->can_id & CAN_EFF_FLAG) + { + Avtp_Can_EnableEff(can_header); + } + + if (cfd->can_id & CAN_EFF_FLAG) + { + Avtp_Can_SetCanIdentifier(can_header, cfd->can_id & CAN_EFF_MASK); + } + else + { + Avtp_Can_SetCanIdentifier(can_header, cfd->can_id & CAN_SFF_MASK); + } + + if (can_is_canfd_skb(skb)) + { + if (cfd->flags & CANFD_BRS) + { + Avtp_Can_EnableBrs(can_header); + } + if (cfd->flags & CANFD_FDF) + { + Avtp_Can_EnableFdf(can_header); + } + if (cfd->flags & CANFD_ESI) + { + Avtp_Can_GetEsi(can_header); + } + } + + // 1722 is a mess. Here we need to pad to quadlets + uint8_t padSize = (AVTP_QUADLET_SIZE - ((AVTP_CAN_HEADER_LEN + cfd->len) % AVTP_QUADLET_SIZE)) % AVTP_QUADLET_SIZE; + Avtp_Can_SetAcfMsgLength(can_header, (AVTP_CAN_HEADER_LEN + cfd->len + padSize) / AVTP_QUADLET_SIZE); + Avtp_Can_SetPad(can_header, padSize); + + pr_debug("Prepared AVTP ACFCAN msg for can id 0x%08x, len %i\n", Avtp_Can_GetCanIdentifier(can_header), cfd->len); + /* + printk(KERN_INFO "Prepared CAN packet for send, ID: 0x%04x ", Avtp_Can_GetCanIdentifier(can_header)); + printk(KERN_CONT " frame data: "); + for (int i = 0; i < cfd->len; i++) + { + printk(KERN_CONT "%02x ", cfd->data[i]); + } + printk(KERN_CONT "\n"); + */ +} + +void calculate_and_set_ntscf_size(ACFCANPdu_t *pdu) +{ + // 1722 is a mess. Bytes, lukicly we have padded quadlets in can already.... + uint16_t canandpadinbytes = Avtp_Can_GetAcfMsgLength(&pdu->can) * AVTP_QUADLET_SIZE; + Avtp_Ntscf_SetNtscfDataLength(&pdu->ntscf, canandpadinbytes); +} + +int forward_can_frame(struct net_device *can_dev, const struct sk_buff *skb_can) +{ + struct acfcan_cfg *cfg = get_acfcan_cfg(can_dev); + if (cfg->eth_netdev == NULL) + { + printk(KERN_INFO "No ethernet device set for ACFCAN device %s\n", can_dev->name); + return -1; + } + + ACFCANPdu_t pdu; + // Init TSCF header + prepare_ntscf_header(&pdu.ntscf, cfg); + prepare_can_header(&pdu.can, cfg, skb_can); + calculate_and_set_ntscf_size(&pdu); + + // Prepare ethernet + struct sk_buff *skb_eth; + struct canfd_frame *cfd = (struct canfd_frame *)skb_can->data; + + // Allocate a socket buffer + skb_eth = alloc_skb(ETH_HLEN + sizeof(ACFCANPdu_t) + cfd->len + Avtp_Can_GetPad(&pdu.can), GFP_KERNEL); + pr_debug("ACFCAN: Allocating skb for ethernet frame: 0x%p\n", (void *)skb_eth->data); + if (!skb_eth) + { + printk(KERN_ERR "Failed to allocate skb\n"); + return -ENOMEM; + } + + skb_reserve(skb_eth, ETH_HLEN); // Reserve space for Ethernet header + unsigned char *data = skb_put(skb_eth, sizeof(pdu)); // Add Headers + memcpy(data, (uint8_t *)&pdu, sizeof(pdu)); // Fill payload with avtp + acf-can header + data = skb_put(skb_eth, cfd->len); // Add payload data + memcpy(data, (uint8_t *)cfd->data, cfd->len); // Fill payload with avtp + acf-can header + + data = skb_put(skb_eth, Avtp_Can_GetPad(&pdu.can)); // Add payload data + memset(data, 0, Avtp_Can_GetPad(&pdu.can)); + + // Set up the Ethernet header + struct ethhdr *eth = (struct ethhdr *)skb_push(skb_eth, sizeof(struct ethhdr)); + + memcpy(eth->h_dest, cfg->dstmac, ETH_ALEN); + memcpy(eth->h_source, cfg->eth_netdev->dev_addr, ETH_ALEN); + eth->h_proto = htons(0x22F0); + + // Set the network device + skb_eth->dev = cfg->eth_netdev; + skb_eth->protocol = eth->h_proto; + skb_eth->ip_summed = CHECKSUM_NONE; + + // Send the frame + pr_debug("ACFCAN sending ethernet frame\n"); + int ret = dev_queue_xmit(skb_eth); + if (ret != NET_XMIT_SUCCESS) + { + printk(KERN_ERR "Failed to send ethernet frame: %d\n", ret); + kfree_skb(skb_eth); + return -1; + } + + return 0; +} + +extern struct list_head acfcaninterface_list; + +int ieee1722_packet_handdler(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + + struct ethhdr *eth = eth_hdr(skb); + if (ntohs(eth->h_proto) != IEEE1722_PROTO) + { + kfree_skb(skb); + return NET_RX_DROP; + } + + // Ignore packets not destined for us + if (skb->pkt_type != PACKET_HOST && skb->pkt_type != PACKET_BROADCAST) + { + kfree_skb(skb); + return NET_RX_DROP; + } + + pr_debug("ETH Received 1722 packet: src=%pM, dst=%pM, proto=0x%04x, type %i, buf is %p with %i refs\n", + eth->h_source, eth->h_dest, ntohs(eth->h_proto), skb->pkt_type, (void *)skb->data, refcount_read(&skb->users)); + + /* + printk(KERN_CONT "Data: "); + for (int i = 0; i < skb->len; i++) + { + printk(KERN_CONT "%02x ", skb->data[i]); + } + printk(KERN_CONT "\n"); + */ + + // Check if this is an ACF-CAN packet + if (skb->len < sizeof(Avtp_Ntscf_t) + sizeof(Avtp_Can_t)) + { + printk(KERN_INFO "ACFCAN short packet, %u > %li\n", skb->len, sizeof(Avtp_Ntscf_t) + sizeof(Avtp_Can_t)); + kfree_skb(skb); + return NET_RX_DROP; + } + + Avtp_CommonHeader_t *common = (Avtp_CommonHeader_t *)skb->data; + if (Avtp_CommonHeader_GetSubtype(common) != AVTP_SUBTYPE_NTSCF) + { + printk(KERN_INFO "ACFCAN: Drop non NTSCF-type %i\n", Avtp_CommonHeader_GetSubtype(common)); + kfree_skb(skb); + return NET_RX_DROP; + } + + Avtp_Ntscf_t *ntscf = (Avtp_Ntscf_t *)skb->data; + Avtp_Can_t *can = (Avtp_Can_t *)(skb->data + sizeof(Avtp_Ntscf_t)); + + // This is bytes, not quadlets + uint16_t msg_length = Avtp_Ntscf_GetNtscfDataLength(ntscf); + + // seq_num = Avtp_Ntscf_GetSequenceNum((Avtp_Ntscf_t*)cf_pdu); + if (msg_length > skb->len - sizeof(Avtp_Ntscf_t)) + { + printk(KERN_INFO "ACFCAN: Drop short packet. NTSCF length %i, packet bytes: %li\n", msg_length, skb->len - sizeof(Avtp_Ntscf_t)); + kfree_skb(skb); + return NET_RX_DROP; + } + + uint64_t stream_id = Avtp_Ntscf_GetStreamId(ntscf); + uint8_t busid = Avtp_Can_GetCanBusId(can); + + pr_debug("ACFCAN: Received valid ACFCAN packet, stream_id=%016llx, busid=%i , msg_length=%i on %s\n", stream_id, busid, msg_length, dev->name); + + // Iterate over all active devices + struct list_head *pos = NULL; + struct acfcan_cfg *cfg = NULL; + struct net_device *can_dev = NULL; + list_for_each(pos, &acfcaninterface_list) + { + cfg = list_entry(pos, struct acfcan_cfg, list); + if (cfg->rx_streamid == stream_id && cfg->canbusId == busid && strcmp(cfg->ethif, dev->name) == 0) + { + pr_debug("ACFCAN Found match, if=%s, stream=%016llx, busid %i\n", cfg->ethif, cfg->rx_streamid, cfg->canbusId); + can_dev = cfg->can_netdev; + break; // Only first match + } + } + + if (can_dev == NULL) + { + printk(KERN_WARNING "No receiving ACFCAN for stream=%016llx, busid %i\n", stream_id, busid); + kfree_skb(skb); + return NET_RX_DROP; + } + + uint8_t is_fd = Avtp_Can_GetFdf(can); + struct sk_buff *can_skb; + struct can_frame *cf; + struct canfd_frame *cfd; + + int err; + + // Allocate a CAN skb + if (is_fd) + { + pr_debug("ACFCAN: Allocating CAN FD skb\n"); + can_skb = alloc_canfd_skb(can_dev, &cfd); + cf = (struct can_frame *)cfd; // This is a bit of a hack, but the beginning of the struct is the same + } + else + { + pr_debug("ACFCAN: Allocating CAN skb\n"); + can_skb = alloc_can_skb(can_dev, &cf); + } + if (!can_skb) + { + printk(KERN_ERR "Failed to allocate CAN skb\n"); + kfree_skb(skb); + return NET_RX_DROP; + } + + cf->can_id = Avtp_Can_GetCanIdentifier(can); + if (Avtp_Can_GetEff(can)) + { + cf->can_id |= CAN_EFF_FLAG; + } + if (Avtp_Can_GetRtr(can)) + { + cf->can_id |= CAN_RTR_FLAG; + } + + if (is_fd) + { + cfd->flags = 0; + if (Avtp_Can_GetBrs(can)) + { + cfd->flags |= CANFD_BRS; + } + if (Avtp_Can_GetFdf(can)) + { + cfd->flags |= CANFD_FDF; + } + if (Avtp_Can_GetEsi(can)) + { + cfd->flags |= CANFD_ESI; + } + cfd->len = msg_length - AVTP_CAN_HEADER_LEN - Avtp_Can_GetPad(can); + } + else + { + cf->len = msg_length - AVTP_CAN_HEADER_LEN - Avtp_Can_GetPad(can); + } + + if (is_fd && cfd->len > CANFD_MAX_DLEN) + { + printk(KERN_ERR "DLC too large for CAN FD\n"); + kfree_skb(skb); + return NET_RX_DROP; + } + else if (!is_fd && cf->len > CAN_MAX_DLEN) + { + printk(KERN_ERR "DLC too large for CAN\n"); + kfree_skb(skb); + return NET_RX_DROP; + } + + if (is_fd) + { + memcpy(cfd->data, skb->data + sizeof(Avtp_Ntscf_t) + sizeof(Avtp_Can_t), cfd->len); + } + else + { + memcpy(cf->data, skb->data + sizeof(Avtp_Ntscf_t) + sizeof(Avtp_Can_t), cf->len); + } + + can_skb->cb[SKB_CB_LOCATION] |= SKB_CB_MINE; // Mark the skb as our own + // Send the CAN skb, disable loop (otherwise the module would receive and forward + // it's own frame) + // TODO: Do we need to free can_skb? + err = can_send(can_skb, 1); + if (err) + { + printk(KERN_ERR "Failed to send CAN skb: %d\n", err); + kfree_skb(skb); + return NET_RX_DROP; + } + + kfree_skb(skb); + return NET_RX_SUCCESS; +} diff --git a/examples/acf-can/linux-kernel-mod/1722ethernet.h b/examples/acf-can/linux-kernel-mod/1722ethernet.h new file mode 100644 index 0000000..69ad15f --- /dev/null +++ b/examples/acf-can/linux-kernel-mod/1722ethernet.h @@ -0,0 +1,27 @@ +#include +#include +#include + +#include "acfcandev.h" + +#include "avtp/acf/Ntscf.h" +#include "avtp/acf/Can.h" +#include "avtp/CommonHeader.h" + +#define CAN_PAYLOAD_LEN 64 +typedef struct +{ + // IEEE 1722 NTSCF header + Avtp_Ntscf_t ntscf; + // IEEE 1722 ACF message #1 + Avtp_Can_t can; +} __attribute__((packed)) ACFCANPdu_t; + +void prepare_ntscf_header(Avtp_Ntscf_t *ntscf_header, struct acfcan_cfg *cfg); +void prepare_can_header(Avtp_Can_t *can_header, struct acfcan_cfg *cfg, const struct sk_buff *skb); +void calculate_and_set_ntscf_size(ACFCANPdu_t *pdu); + +int forward_can_frame(struct net_device *can_dev, const struct sk_buff *skb); + +int ieee1722_packet_handdler(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev); diff --git a/examples/acf-can/linux-kernel-mod/Makefile b/examples/acf-can/linux-kernel-mod/Makefile new file mode 100644 index 0000000..023fb28 --- /dev/null +++ b/examples/acf-can/linux-kernel-mod/Makefile @@ -0,0 +1,11 @@ +obj-$(CONFIG_ACF_CAN) += acfcan.o +acfcan-objs := acfcanmain.o 1722ethernet.o ../../../src/avtp/acf/Tscf.o ../../../src/avtp/acf/Ntscf.o ../../../src/avtp/acf/Can.o ../../../src/avtp/Utils.o ../../../src/avtp/CommonHeader.o +ccflags-y += -DLINUX_KERNEL1722=1 +ccflags-y += -I $(src)/../../../include + + + +all: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean \ No newline at end of file diff --git a/examples/acf-can/linux-kernel-mod/Readme.md b/examples/acf-can/linux-kernel-mod/Readme.md new file mode 100644 index 0000000..358af38 --- /dev/null +++ b/examples/acf-can/linux-kernel-mod/Readme.md @@ -0,0 +1,134 @@ +# ACF CAN kernel module + +> [!CAUTION] +> Open1722 is in active development. This kernel module is a fun experiment on top of that. Note that kernel modules can crash your computer in fun and unexpected ways _anytime_. + + +This wrap Open1722 ACF-CAN functionality in a Linux CAN kernel module. That means your applications can use the normal Linux socketcan interface (like using HW CAN interfaces or vcan), but will speak ACF-CAN via Ethernet directly. +Compared to the [Linux user space examples](../linux) the kernel module leads to less user/kernelspace switches, and you do not need to deal with addtional user space processes. + +## Architecture + +With this module, from user space perspective there is no difference between a real CAN device supported by a kernel driver or speaking IEEE 1722 acf-can over any ethernet-like interface supported by the kernel. + +![ACF CAN Kernel Architecture](acfcankernel.svg) + +## Features + +### Supported + - CAN and CAN FD + - configurable stream and bus id + - NTSCF + - ACF-CAN + - Sending and receiving + +### Not (yet) supported + - unidirectional operation (RX or TX only) + - CAN-BRIEF + - TSCF + - batching. currently only ONE ACF-CAN message per 1722 frame is allowed + + +## How to compile + +Make sure you are generally setup to compile kernel modules, e.g. in Ubuntu something like + +``` +sudo apt update && +sudo apt-get install gcc make build-essential libncurses-dev exuberant-ctags build-essential linux-headers-`uname -r` +``` + +Then in this folder do + +``` +export CONFIG_ACF_CAN=m +make +``` + +### Building failed +Likely building fails for you with a message like + +``` +ERROR: modpost: GPL-incompatible module acfcan.ko uses GPL-only symbol 'skb_tstamp_tx' +``` +The reason is, currently we are only able to release BSD licensed ode in this repo, however in the kernel we need to use interfaces that are only allowed to be used from GPL-licensed code. Luckily _YOU_ are free to use BSD code in GPL context on your computer. So take a look at `MODULE_LICENSE` in [./acfcanmodulemetadata.h]()./acfcanmodulemetadata.h). Hint: `Dual BSD/GPL` + +## Using the module + +### Loading the module + +If you load the module the first time from a local folder, and your kernel has not yet loaded anything form the CAN subsystem you will get missing symbols. Easy fix, just load the `can` module via mpdprobe, whoch will make sure all necessary dependenecies are pulled into into the kernel + +``` +sudo modprobe can +``` + +Afterwards load module with + +``` +sudo insmod acfcan.ko +``` + +### Secure Boot + +If you have a secure boot system you may not be able to load unsigned kernel modules. See [https://ubuntu.com/blog/how-to-sign-things-for-secure-boot](https://ubuntu.com/blog/how-to-sign-things-for-secure-boot) for hints to fix that. + +### Example + +You can add a number of acfcan interfaces. Some aspects like destination MAC or ethernet infterface to be used need to be configured via sysfs _after_ the interface is created, but _before_ it is set to up. + +This is a complete example to set up 2 acf-can interfaces connected via two _virtual_ ethernet interfaces on a single machine. + +First set up a pair of virtual ethernet interfaced as in [./instalmon.sh](./installmon.sh): + +```sh +ip link add dev mon1 type veth peer name mon2 +ip link set dev mon2 up +ip link set dev mon1 up +``` + +Then we set up the two Open1722 ACF-CAN interfaces as in [./myup.sh](./myup.sh): + +```sh +sudo ip link add dev ecu1 type acfcan +sudo ip link add dev ecu2 type acfcan + +sudo ip link set ecu1 mtu 72 +sudo ip link set ecu2 mtu 72 + +sudo echo -n "mon1" | sudo tee /sys/class/net/ecu1/acfcan/ethif +sudo echo -n "mon2" | sudo tee /sys/class/net/ecu2/acfcan/ethif + +sudo echo -n $(cat /sys/class/net/mon2/address) | sudo tee /sys/class/net/ecu1/acfcan/dstmac +sudo echo -n $(cat /sys/class/net/mon1/address) | sudo tee /sys/class/net/ecu2/acfcan/dstmac + + +sudo echo -n "cafe11" | sudo tee /sys/class/net/ecu1/acfcan/tx_streamid +sudo echo -n "dead22" | sudo tee /sys/class/net/ecu2/acfcan/tx_streamid + +sudo echo -n "dead22" | sudo tee /sys/class/net/ecu1/acfcan/rx_streamid +sudo echo -n "cafe11" | sudo tee /sys/class/net/ecu2/acfcan/rx_streamid + +sudo ip link set up ecu1 +sudo ip link set up ecu2 +``` + +Note that IEEE-1722 specific options for an ACF-CAn device `` can be set via sysfs in the folder `/sys/class/net//acfcan`. +This can only be done when the interface is (still) _down_. You can always change options by downing the interface, changing the desired option and bringing it up again. + +Keep an eye out in kernel logs via `dmesg --follow` for any problems. + +once everything is setup you can use `cansend` and `candump` to see every message you sent to ACF-CAN interface `ecu1` being replicated on `ecu2` and vice versa. + +With tcpdump or wireshark you can see the IEEE-1722 frames on the `mon1` and `mon2` interfaces. + +To really distribute this on two machines, set up interface `ecu1` on the first machine, using the real ethernet inferface for the `ethif` option and do the same for `ecu2` on the second machine. + +## Debugging +If your kernel supports [dynamic debugging](https://www.kernel.org/doc/html/latest/admin-guide/dynamic-debug-howto.html) you can enable debug messages from the kernel module by doing + +``` +echo 'module acfcan +p'> /sys/kernel/debug/dynamic_debug/control +``` + +after loading it. diff --git a/examples/acf-can/linux-kernel-mod/acfcandev.h b/examples/acf-can/linux-kernel-mod/acfcandev.h new file mode 100644 index 0000000..8e9cff9 --- /dev/null +++ b/examples/acf-can/linux-kernel-mod/acfcandev.h @@ -0,0 +1,67 @@ +/* acfcan.c - Virtual IEEE 1722 acf-can CAN interface + * + * Copyright (c) 2024 COVESA Open1722 + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of COVESA nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#pragma once + +#include +#include +#include +#include + +#define IEEE1722_PROTO 0x22f0 + +#define TX_ENABLE (1 << 7) +#define RX_ENABLE (1 << 6) + +// this is more guesswork. We need some space and it seems +// raw can is using one int.... Not sure what would happen with +// ISO-TP and such things.... +#define SKB_CB_LOCATION 4 +#define SKB_CB_MINE (1 << 7) + +/* Private per-device configuration */ +struct acfcan_cfg +{ + struct list_head list; // we need a list so we can map received ethernet packets + __u8 dstmac[6]; // send acf-can frames to this mac + __u64 rx_streamid; // listen to this acf-can stream-id + __u64 tx_streamid; // send acf-can frames with this stream-id + __u8 flags; + __u8 sequenceNum; + __u8 canbusId; + char ethif[IFNAMSIZ]; + struct net_device *eth_netdev; // this is the eth if used for sending and receiving + struct net_device *can_netdev; // this is the (virtual) can if + netdevice_tracker tracker; +}; + +// get the acfcan_cfg struct from the device +#define get_acfcan_cfg(dev) ((struct acfcan_cfg *)((char *)(can_get_ml_priv(dev)) + sizeof(struct can_ml_priv))) diff --git a/examples/acf-can/linux-kernel-mod/acfcandevsysfs.h b/examples/acf-can/linux-kernel-mod/acfcandevsysfs.h new file mode 100644 index 0000000..2fd8dfe --- /dev/null +++ b/examples/acf-can/linux-kernel-mod/acfcandevsysfs.h @@ -0,0 +1,243 @@ +/* acfcan.c - Virtual IEEE 1722 acf-can CAN interface + * + * Copyright (c) 2024 COVESA Open1722 + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of COVESA nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#pragma once + +#include + +#define NOCHANGE_IF_UP(what) ({ \ + if (netif_running(net_dev)) \ + { \ + printk(KERN_WARNING "ACFCAN: Cannot change %s while device %s is up\n", what, net_dev->name); \ + return -EBUSY; \ + } \ +}) + +static ssize_t dstmac_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + if (count == 1 && *buf == '\n') + { + return 1; + } + struct net_device *net_dev = to_net_dev(dev); + + NOCHANGE_IF_UP("destination MAC"); + struct acfcan_cfg *cfg = get_acfcan_cfg(net_dev); + + // Handle the input value here + __u8 newmac[6] = {0, 1, 2, 3, 4, 5}; + int rc = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", &newmac[0], &newmac[1], &newmac[2], &newmac[3], &newmac[4], &newmac[5]); + if (rc != 6) + { + printk(KERN_WARNING "ACFCAN Can not set destination MAC for %s to %s\n", net_dev->name, buf); + return -EINVAL; + } + memcpy(cfg->dstmac, newmac, 6); + pr_debug("ACFCAN: Setting destination MAC for %s to %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx \n", net_dev->name, newmac[0], newmac[1], newmac[2], newmac[3], newmac[4], newmac[5]); + return 17; +} + +static ssize_t dstmac_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + pr_debug("ACFCAN: Reading destination MAC\n"); + struct net_device *net_dev = to_net_dev(dev); + struct acfcan_cfg *cfg = get_acfcan_cfg(net_dev); + + return sprintf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", cfg->dstmac[0], cfg->dstmac[1], cfg->dstmac[2], cfg->dstmac[3], cfg->dstmac[4], cfg->dstmac[5]); +} + +// Only store the interface name. we will check if it is valid later when the interface is opened/upped +static ssize_t ethif_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + if (count == 1 && *buf == '\n') + { + return 1; + } + + struct net_device *net_dev = to_net_dev(dev); + + NOCHANGE_IF_UP("ethernet interface"); + + struct acfcan_cfg *cfg = get_acfcan_cfg(net_dev); + + if (count >= (IFNAMSIZ - 1)) + { + printk(KERN_WARNING "ACFCAN: Interface name too long\n"); + return -EINVAL; + } + + memcpy(cfg->ethif, buf, count); + cfg->ethif[count] = '\0'; + + pr_debug("ACFCAN Storing interface %s for %s\n", buf, net_dev->name); + return count; +} + +static ssize_t ethif_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct net_device *net_dev = to_net_dev(dev); + struct acfcan_cfg *cfg = get_acfcan_cfg(net_dev); + + if (cfg->eth_netdev) + { + return sprintf(buf, "%s", cfg->eth_netdev->name); + } + else + { + buf[0] = '\0'; + return 0; + } +} + +static ssize_t tx_streamid_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + if (count == 1 && *buf == '\n') + { + return 1; + } + + struct net_device *net_dev = to_net_dev(dev); + + NOCHANGE_IF_UP("tx stream id"); + + struct acfcan_cfg *cfg = get_acfcan_cfg(net_dev); + + __u64 tx_streamid; + int rc = sscanf(buf, "%llx", &tx_streamid); + if (!rc) + { + printk(KERN_WARNING "ACFCAN: Invalid stream id\n"); + return -EINVAL; + } + + cfg->tx_streamid = tx_streamid; + + pr_debug("ACFCAN TX streamid 0x%016llx for %s\n", tx_streamid, net_dev->name); + return count; +} + +static ssize_t rx_streamid_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + if (count == 1 && *buf == '\n') + { + return 1; + } + + struct net_device *net_dev = to_net_dev(dev); + + NOCHANGE_IF_UP("rx stream id"); + + struct acfcan_cfg *cfg = get_acfcan_cfg(net_dev); + + __u64 rx_streamid; + int rc = sscanf(buf, "%llx", &rx_streamid); + if (!rc) + { + printk(KERN_WARNING "ACFCAN: Invalid stream id\n"); + return -EINVAL; + } + + cfg->rx_streamid = rx_streamid; + + pr_debug("ACFCAN RX streamid 0x%016llx for %s\n", rx_streamid, net_dev->name); + return count; +} + +static ssize_t busid_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + if (count == 1 && *buf == '\n') + { + return 1; + } + + struct net_device *net_dev = to_net_dev(dev); + + NOCHANGE_IF_UP("bus id"); + + struct acfcan_cfg *cfg = get_acfcan_cfg(net_dev); + + __u8 busid; + int rc = sscanf(buf, "%hhu", &busid); + if (!rc) + { + printk(KERN_WARNING "ACFCAN: Invalid bus id\n"); + return -EINVAL; + } + + cfg->canbusId = busid; + + pr_debug("ACFCAN setting busid to %u for %s\n", busid, net_dev->name); + return count; +} + +static ssize_t rx_streamid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct net_device *net_dev = to_net_dev(dev); + struct acfcan_cfg *cfg = get_acfcan_cfg(net_dev); + + return sprintf(buf, "0x%016llx", cfg->rx_streamid); +} + +static ssize_t tx_streamid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct net_device *net_dev = to_net_dev(dev); + struct acfcan_cfg *cfg = get_acfcan_cfg(net_dev); + + return sprintf(buf, "0x%016llx", cfg->tx_streamid); +} + +static ssize_t busid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct net_device *net_dev = to_net_dev(dev); + struct acfcan_cfg *cfg = get_acfcan_cfg(net_dev); + + return sprintf(buf, "%i", cfg->canbusId); +} + +static DEVICE_ATTR_RW(dstmac); +static DEVICE_ATTR_RW(ethif); +static DEVICE_ATTR_RW(rx_streamid); +static DEVICE_ATTR_RW(tx_streamid); +static DEVICE_ATTR_RW(busid); + +static struct attribute *dev_attrs[] = { + &dev_attr_dstmac.attr, + &dev_attr_ethif.attr, + &dev_attr_tx_streamid.attr, + &dev_attr_rx_streamid.attr, + &dev_attr_busid.attr, + NULL, /* NULL-terminated list */ +}; + +static struct attribute_group dev_attr_group = { + .name = "acfcan", + .attrs = dev_attrs, +}; diff --git a/examples/acf-can/linux-kernel-mod/acfcankernel.svg b/examples/acf-can/linux-kernel-mod/acfcankernel.svg new file mode 100644 index 0000000..09e8370 --- /dev/null +++ b/examples/acf-can/linux-kernel-mod/acfcankernel.svg @@ -0,0 +1,4 @@ + + + +
Linux
 Userspace
acfcan0
can0
Application
Linux socketcan interface
Linux
 Kernelspace
ACF-CAN device
CAN
 device
CAN HW driver
Open1722 
ACF-CAN driver
vcan0
VCAN
 device



VCAN
Ethernet driver
CAN Frames
IEEE 1722 frames
\ No newline at end of file diff --git a/examples/acf-can/linux-kernel-mod/acfcanmain.c b/examples/acf-can/linux-kernel-mod/acfcanmain.c new file mode 100644 index 0000000..6f3a33b --- /dev/null +++ b/examples/acf-can/linux-kernel-mod/acfcanmain.c @@ -0,0 +1,319 @@ +/* acfcan.c - Virtual IEEE 1722 acf-can CAN interface + * + * Copyright (c) 2024 COVESA Open1722 + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of COVESA nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +// TODO Add GPL exception like vcan and modify SPDX with OR + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "1722ethernet.h" +#include "acfcandev.h" +#include "acfcandevsysfs.h" + +#include "acfcanmodulemetadata.h" + +char *version = "2016"; + +static struct packet_type ieee1722_packet_type; + +LIST_HEAD(acfcaninterface_list); + +static void acfcan_rx(struct sk_buff *skb, struct net_device *dev) +{ + struct net_device_stats *stats = &dev->stats; + printk(KERN_INFO "ACF-CAN RX\n"); + stats->rx_packets++; + stats->rx_bytes += can_skb_get_data_len(skb); + + skb->pkt_type = PACKET_HOST; + skb->dev = dev; + skb->ip_summed = CHECKSUM_UNNECESSARY; + + netif_rx(skb); +} + +static netdev_tx_t acfcan_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct net_device_stats *stats = &dev->stats; + unsigned int len; + int loop; + bool echo = false; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + len = can_skb_get_data_len(skb); + stats->tx_packets++; + stats->tx_bytes += len; + + /* set flag whether this packet has to be looped back */ + loop = skb->pkt_type == PACKET_LOOPBACK; + + // This means we generated the frame ourself so we should NOT + // forward it to the TX stream + uint8_t mine = skb->cb[SKB_CB_LOCATION] & SKB_CB_MINE; + + skb_tx_timestamp(skb); + + if (!mine) + { + // Forward the frame to the TX stream + forward_can_frame(dev, skb); + } + + if (!echo) + { + /* no echo handling available inside this driver */ + if (loop) + { + /* only count the packets here, because the + * CAN core already did the echo for us + */ + stats->rx_packets++; + stats->rx_bytes += len; + } + consume_skb(skb); + return NETDEV_TX_OK; + } + + /* perform standard echo handling for CAN network interfaces */ + if (loop) + { + skb = can_create_echo_skb(skb); + if (!skb) + return NETDEV_TX_OK; + + /* receive with packet counting */ + acfcan_rx(skb, dev); + } + else + { + /* no looped packets => no counting */ + consume_skb(skb); + } + return NETDEV_TX_OK; +} + +static int acfcan_change_mtu(struct net_device *dev, int new_mtu) +{ + /* Do not allow changing the MTU while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + + if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU && + !can_is_canxl_dev_mtu(new_mtu)) + return -EINVAL; + + WRITE_ONCE(dev->mtu, new_mtu); + return 0; +} + +static int acfcan_up(struct net_device *dev) +{ + struct acfcan_cfg *cfg = get_acfcan_cfg(dev); + + // chek we have an a valid ethernet device + struct net_device *ethif; + + ethif = netdev_get_by_name(&init_net, cfg->ethif, &cfg->tracker, GFP_KERNEL); + + if (!ethif) + { + printk(KERN_WARNING "ACFCAN Can not use interface %s for %s\n", cfg->ethif, dev->name); + return -EINVAL; + } + + cfg->eth_netdev = ethif; + + list_add(&cfg->list, &acfcaninterface_list); + + printk(KERN_INFO "ACFCAN interface %s on %s up. TX-streamid 0x%llX, RX-streamid 0x%0llX, bus-id %i.\n", dev->name, cfg->ethif, cfg->tx_streamid, cfg->rx_streamid, cfg->canbusId); + return 0; +} + +static int acfcan_down(struct net_device *dev) +{ + struct acfcan_cfg *cfg = get_acfcan_cfg(dev); + if (cfg->eth_netdev) + { + netdev_put(cfg->eth_netdev, &cfg->tracker); + } + list_del(&cfg->list); + printk(KERN_INFO "ACFCAN interface %s down\n", dev->name); + return 0; +} + +static const struct net_device_ops acfcan_netdev_ops = { + .ndo_start_xmit = acfcan_tx, + .ndo_change_mtu = acfcan_change_mtu, + .ndo_open = acfcan_up, + .ndo_stop = acfcan_down, +}; + +static const struct ethtool_ops acfcan_ethtool_ops = { + .get_ts_info = ethtool_op_get_ts_info, +}; + +// The default stuff. Newlink can do more +static void acfcan_setup(struct net_device *dev) +{ + bool echo = false; + dev->type = ARPHRD_CAN; + dev->mtu = CANFD_MTU; + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->tx_queue_len = 0; + dev->flags = IFF_NOARP; + can_set_ml_priv(dev, netdev_priv(dev)); + + void *canpriv = can_get_ml_priv(dev); + pr_debug("Setting up new acfcan device %s, priv is at %p \n", dev->name, canpriv); + + /* set flags according to driver capabilities */ + if (echo) + dev->flags |= IFF_ECHO; + + dev->netdev_ops = &acfcan_netdev_ops; + dev->ethtool_ops = &acfcan_ethtool_ops; + dev->needs_free_netdev = true; + + struct acfcan_cfg *cfg = get_acfcan_cfg(dev); + cfg->rx_streamid = 0xbbbb; + cfg->tx_streamid = 0xaaaa; + cfg->dstmac[0] = 0xff; + cfg->dstmac[1] = 0xff; + cfg->dstmac[2] = 0xff; + cfg->dstmac[3] = 0xff; + cfg->dstmac[4] = 0xff; + cfg->dstmac[5] = 0xff; + cfg->eth_netdev = NULL; + cfg->can_netdev = dev; + cfg->ethif[0] = '\0'; // this is a string so setting first byte to 0 is fine + cfg->sequenceNum = 0; + cfg->canbusId = 0; + cfg->flags = TX_ENABLE | RX_ENABLE; // todo: Make configurable +} + +// is this deleting or downing the interfae? +static void acfcan_remove(struct net_device *dev, struct list_head *head) +{ + // Remove the custom sysfs attribute + device_remove_file(&dev->dev, &dev_attr_dstmac); + struct acfcan_cfg *cfg = get_acfcan_cfg(dev); + if (cfg->eth_netdev) + { + netdev_put(cfg->eth_netdev, &cfg->tracker); + } + + unregister_netdevice(dev); + + printk(KERN_INFO "ACFCAN interface %s unregistered\n", dev->name); +} + +static int acfcan_newlink(struct net *net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + int err = register_netdevice(dev); + + if (err) + { + printk(KERN_ERR "Failed to register acfcan netdevice %s\n", dev->name); + return err; + } + + pr_debug("Creating group for device %s\n", dev->name); + int ret = sysfs_create_group(&dev->dev.kobj, &dev_attr_group); + if (ret) + { + pr_err("Failed to create sysfs group for net_device\n"); + } + + printk(KERN_INFO "Registered new ACFCAN device %s\n", dev->name); + + return 0; +} + +// .dellink = acfcan_remove, ? +static struct rtnl_link_ops acfcan_link_ops __read_mostly = { + .kind = DRV_NAME, + .priv_size = sizeof(struct can_ml_priv) + sizeof(struct acfcan_cfg), + .setup = acfcan_setup, + .newlink = acfcan_newlink, + .dellink = acfcan_remove, +}; + +static int __init init_acfcan(void) +{ + if (strcmp(version, "2016") == 0) + { + printk(KERN_INFO "ACFCAN version: %s\n", version); + } + else + { + printk(KERN_ERR "ACFCAN unsupported 1722 version %s\n", version); + return -1; + } + + // We want all the 1722 packets. < + ieee1722_packet_type.type = htons(IEEE1722_PROTO); + ieee1722_packet_type.func = ieee1722_packet_handdler; + ieee1722_packet_type.dev = NULL; + dev_add_pack(&ieee1722_packet_type); + + return rtnl_link_register(&acfcan_link_ops); +} + +static void __exit cleanup_acfcan(void) +{ + pr_info("Unloading ACFCAN\n"); + dev_remove_pack(&ieee1722_packet_type); + rtnl_link_unregister(&acfcan_link_ops); +} + +module_init(init_acfcan); +module_exit(cleanup_acfcan); + +module_param(version, charp, 0); +MODULE_PARM_DESC(version, "IEEE-1722 version"); diff --git a/examples/acf-can/linux-kernel-mod/acfcanmodulemetadata.h b/examples/acf-can/linux-kernel-mod/acfcanmodulemetadata.h new file mode 100644 index 0000000..61f99fd --- /dev/null +++ b/examples/acf-can/linux-kernel-mod/acfcanmodulemetadata.h @@ -0,0 +1,17 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + */ + +#pragma once + +#include + + +#define DRV_NAME "acfcan" + +// Module metadata +MODULE_AUTHOR("Sebastian Schildt"); +MODULE_DESCRIPTION("IEEE-1722 ACF-CAN bridge"); +MODULE_LICENSE("BSD3"); + +MODULE_ALIAS_RTNL_LINK(DRV_NAME); \ No newline at end of file diff --git a/examples/acf-can/linux-kernel-mod/installmon.sh b/examples/acf-can/linux-kernel-mod/installmon.sh new file mode 100755 index 0000000..7f4b0c8 --- /dev/null +++ b/examples/acf-can/linux-kernel-mod/installmon.sh @@ -0,0 +1,3 @@ +ip link add dev mon1 type veth peer name mon2 +ip link set dev mon2 up +ip link set dev mon1 up diff --git a/examples/acf-can/linux-kernel-mod/myup.sh b/examples/acf-can/linux-kernel-mod/myup.sh new file mode 100755 index 0000000..f994217 --- /dev/null +++ b/examples/acf-can/linux-kernel-mod/myup.sh @@ -0,0 +1,28 @@ + +# Simulate two ecus bridged + + +sudo ip link add dev ecu1 type acfcan +sudo ip link add dev ecu2 type acfcan + +sudo ip link set ecu1 mtu 72 +sudo ip link set ecu2 mtu 72 + +sudo echo -n "mon1" | sudo tee /sys/class/net/ecu1/acfcan/ethif +sudo echo -n "mon2" | sudo tee /sys/class/net/ecu2/acfcan/ethif + +sudo echo -n $(cat /sys/class/net/mon2/address) | sudo tee /sys/class/net/ecu1/acfcan/dstmac +sudo echo -n $(cat /sys/class/net/mon1/address) | sudo tee /sys/class/net/ecu2/acfcan/dstmac + + +sudo echo -n "cafe11" | sudo tee /sys/class/net/ecu1/acfcan/tx_streamid +sudo echo -n "dead22" | sudo tee /sys/class/net/ecu2/acfcan/tx_streamid + +sudo echo -n "dead22" | sudo tee /sys/class/net/ecu1/acfcan/rx_streamid +sudo echo -n "cafe11" | sudo tee /sys/class/net/ecu2/acfcan/rx_streamid + +sudo ip link set up ecu1 +sudo ip link set up ecu2 + + + diff --git a/examples/acf-can/linux-kernel-mod/up.sh b/examples/acf-can/linux-kernel-mod/up.sh new file mode 100755 index 0000000..fcf3b19 --- /dev/null +++ b/examples/acf-can/linux-kernel-mod/up.sh @@ -0,0 +1,4 @@ +sudo ip link add dev acfcan0 type acfcan +sudo ip link set acfcan0 mtu 16 +sudo echo -n "lo" | sudo tee /sys/class/net/acfcan0/acfcan/ethif +sudo ip link set up acfcan0 diff --git a/examples/acf-can/linux-kernel-mod/upfd.sh b/examples/acf-can/linux-kernel-mod/upfd.sh new file mode 100755 index 0000000..8ef611d --- /dev/null +++ b/examples/acf-can/linux-kernel-mod/upfd.sh @@ -0,0 +1,4 @@ +sudo ip link add dev acfcan1 type acfcan +sudo ip link set acfcan1 mtu 72 +sudo echo -n "lo" | sudo tee /sys/class/net/acfcan1/acfcan/ethif +sudo ip link set up acfcan1 diff --git a/include/avtp/Byteorder.h b/include/avtp/Byteorder.h index 67d3752..d0e8932 100644 --- a/include/avtp/Byteorder.h +++ b/include/avtp/Byteorder.h @@ -28,7 +28,11 @@ */ #pragma once +#ifdef LINUX_KERNEL1722 +#include +#else #include +#endif #ifdef __cplusplus extern "C" { diff --git a/include/avtp/CommonHeader.h b/include/avtp/CommonHeader.h index 3ffc89c..8b7b4c3 100644 --- a/include/avtp/CommonHeader.h +++ b/include/avtp/CommonHeader.h @@ -36,8 +36,6 @@ #pragma once -#include - #include "avtp/Defines.h" #ifdef __cplusplus diff --git a/include/avtp/Defines.h b/include/avtp/Defines.h index 09f39b7..359c5c8 100644 --- a/include/avtp/Defines.h +++ b/include/avtp/Defines.h @@ -29,8 +29,11 @@ #pragma once +#ifdef LINUX_KERNEL1722 +#include +#else #include - +#endif #ifdef __cplusplus extern "C" { #endif diff --git a/include/avtp/Utils.h b/include/avtp/Utils.h index bf8930e..80bba5a 100644 --- a/include/avtp/Utils.h +++ b/include/avtp/Utils.h @@ -36,7 +36,6 @@ #pragma once -#include #include "avtp/Defines.h" #include "avtp/Byteorder.h" diff --git a/include/avtp/acf/AcfCommon.h b/include/avtp/acf/AcfCommon.h index d0ee12d..5c94327 100644 --- a/include/avtp/acf/AcfCommon.h +++ b/include/avtp/acf/AcfCommon.h @@ -34,7 +34,6 @@ #pragma once -#include #include "avtp/Defines.h" #ifdef __cplusplus diff --git a/include/avtp/acf/Can.h b/include/avtp/acf/Can.h index 054c94c..e2423d9 100644 --- a/include/avtp/acf/Can.h +++ b/include/avtp/acf/Can.h @@ -35,7 +35,6 @@ #pragma once -#include #include "avtp/Defines.h" #include "avtp/acf/AcfCommon.h" @@ -49,7 +48,7 @@ extern "C" { typedef struct { uint8_t header[AVTP_CAN_HEADER_LEN]; uint8_t payload[0]; -} Avtp_Can_t; +} __attribute__((packed)) Avtp_Can_t; typedef enum { AVTP_CAN_CLASSIC = 0, diff --git a/include/avtp/acf/Ntscf.h b/include/avtp/acf/Ntscf.h index 4966bb1..8923b4e 100644 --- a/include/avtp/acf/Ntscf.h +++ b/include/avtp/acf/Ntscf.h @@ -35,7 +35,11 @@ #pragma once +#ifdef LINUX_KERNEL1722 +#include +#else #include +#endif #include "avtp/Defines.h" @@ -48,7 +52,7 @@ extern "C" { typedef struct { uint8_t header[AVTP_NTSCF_HEADER_LEN]; uint8_t payload[0]; -} Avtp_Ntscf_t; +} __attribute__((packed)) Avtp_Ntscf_t; typedef enum { AVTP_NTSCF_FIELD_SUBTYPE, diff --git a/include/avtp/acf/Tscf.h b/include/avtp/acf/Tscf.h index a434258..81c275a 100644 --- a/include/avtp/acf/Tscf.h +++ b/include/avtp/acf/Tscf.h @@ -35,7 +35,6 @@ #pragma once -#include #include "avtp/Defines.h" diff --git a/src/avtp/CommonHeader.c b/src/avtp/CommonHeader.c index 8fc4579..55a5746 100644 --- a/src/avtp/CommonHeader.c +++ b/src/avtp/CommonHeader.c @@ -27,8 +27,13 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include -#include +#ifdef LINUX_KERNEL1722 + #include + #include +#else + #include + #include +#endif #include "avtp/CommonHeader.h" #include "avtp/Utils.h" diff --git a/src/avtp/Utils.c b/src/avtp/Utils.c index 7cab2bf..c09a84c 100644 --- a/src/avtp/Utils.c +++ b/src/avtp/Utils.c @@ -27,9 +27,13 @@ * SPDX-License-Identifier: BSD-3-Clause */ +#ifdef LINUX_KERNEL1722 +#include +#else #include -#include -#include +#include +#endif + #include "avtp/Utils.h" #include "avtp/Defines.h" @@ -38,7 +42,7 @@ #define MAX(x, y) (((x) > (y)) ? (x) : (y)) #define MIN(x, y) (((x) < (y)) ? (x) : (y)) -int IsFieldDescriptorValid(const Avtp_FieldDescriptor_t* fieldDescriptor) +static int IsFieldDescriptorValid(const Avtp_FieldDescriptor_t* fieldDescriptor) { return fieldDescriptor->bits <= 64 && fieldDescriptor->offset <= 31; } diff --git a/src/avtp/acf/Can.c b/src/avtp/acf/Can.c index a26b249..71298b6 100644 --- a/src/avtp/acf/Can.c +++ b/src/avtp/acf/Can.c @@ -27,8 +27,12 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include + +#ifdef LINUX_KERNEL1722 +#include +#else #include +#endif #include "avtp/acf/Can.h" #include "avtp/Utils.h" diff --git a/src/avtp/acf/Ntscf.c b/src/avtp/acf/Ntscf.c index 81704a8..11767d5 100644 --- a/src/avtp/acf/Ntscf.c +++ b/src/avtp/acf/Ntscf.c @@ -27,8 +27,13 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include -#include +#ifdef LINUX_KERNEL1722 + #include + #include +#else + #include + #include +#endif #include "avtp/acf/Ntscf.h" #include "avtp/Utils.h" diff --git a/src/avtp/acf/Tscf.c b/src/avtp/acf/Tscf.c index 4cf8b56..69e2f00 100644 --- a/src/avtp/acf/Tscf.c +++ b/src/avtp/acf/Tscf.c @@ -27,8 +27,14 @@ * SPDX-License-Identifier: BSD-3-Clause */ -#include +#ifdef LINUX_KERNEL1722 +#include +#include +#else +#include #include +#endif + #include "avtp/acf/Tscf.h" #include "avtp/Utils.h"