Compare commits
32 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
c4828c486d | |
|
|
8ca5fa7fe9 | |
|
|
5123a30746 | |
|
|
1bdb4bbb50 | |
|
|
8b5d92d61d | |
|
|
8618f2e75e | |
|
|
7158af4b73 | |
|
|
d66977868f | |
|
|
9dd9a74b9e | |
|
|
760b9d8e34 | |
|
|
c888a206d9 | |
|
|
b69aaf12ab | |
|
|
4841b2863c | |
|
|
8f5889e129 | |
|
|
f92a946a08 | |
|
|
9caa973a5a | |
|
|
67c196ddfe | |
|
|
2ee6101ec2 | |
|
|
f67b4b3620 | |
|
|
cd073b2019 | |
|
|
198cc74e21 | |
|
|
e455a8847c | |
|
|
5d25e80c35 | |
|
|
6e9319c3f4 | |
|
|
7c2338125f | |
|
|
7924117c2a | |
|
|
c515de3a25 | |
|
|
a46b3c62ed | |
|
|
8a21b53cd1 | |
|
|
9da27da69e | |
|
|
6bbcf1b527 | |
|
|
668e50d08c |
|
|
@ -98,3 +98,7 @@ extra_certificates
|
||||||
signing_key.priv
|
signing_key.priv
|
||||||
signing_key.x509
|
signing_key.x509
|
||||||
x509.genkey
|
x509.genkey
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
compile_commands.json
|
||||||
|
intrepid.mod
|
||||||
39
CHANGELOG
39
CHANGELOG
|
|
@ -1,3 +1,42 @@
|
||||||
|
v3.1.2
|
||||||
|
Update kernel logging for dropped messages to pr_debug
|
||||||
|
Added instructions for debug message in README
|
||||||
|
v3.1.1
|
||||||
|
Update copyright
|
||||||
|
Fix Ethernet interfaces
|
||||||
|
|
||||||
|
v3.0.4
|
||||||
|
Update copyright
|
||||||
|
Add Fedora package requirements to README
|
||||||
|
Fix for newer 6.4.x Kernel, class_create() sig changed (Doug Potts <dpotts@genrad.com>)
|
||||||
|
|
||||||
|
v3.0.3
|
||||||
|
Update daemon version requirement to 3.1.*
|
||||||
|
|
||||||
|
v3.0.2
|
||||||
|
Bump maximum device count to 64
|
||||||
|
|
||||||
|
v3.0.1
|
||||||
|
Update copyright
|
||||||
|
|
||||||
|
v3.0.0
|
||||||
|
Added Functionality for Ethernet
|
||||||
|
|
||||||
|
v2.1.1
|
||||||
|
Fix support for kernels <5.12
|
||||||
|
Tested on 5.4.0
|
||||||
|
|
||||||
|
v2.1.0
|
||||||
|
Implement proper functionality for echoing transmits
|
||||||
|
Transmit errors are now reported properly
|
||||||
|
Transmit byte count is now reported properly
|
||||||
|
Use with icsscand >= v2.1.0 for full echo support
|
||||||
|
|
||||||
|
v2.0.5
|
||||||
|
Add support for kernels 5.9+
|
||||||
|
Tested on 5.11.0-rc5
|
||||||
|
CAN frames with length > 8 from userspace are rejected rather than truncated
|
||||||
|
|
||||||
v2.0.4
|
v2.0.4
|
||||||
Ensure carrier is up on new kernels which require it
|
Ensure carrier is up on new kernels which require it
|
||||||
|
|
||||||
|
|
|
||||||
55
README.md
55
README.md
|
|
@ -1,10 +1,10 @@
|
||||||
Version 2.0.4
|
Version 3.1.2
|
||||||
|
|
||||||
This is the kernel object portion of the Intrepid Control Systems SocketCAN support. For SocketCAN to work with Intrepid devices you will need to have this kernel object loaded on your system. Once the module is built and loaded run [icsscand](https://github.com/intrepidcs/icsscand) to turn on SocketCAN support.
|
This is the kernel object portion of the Intrepid Control Systems SocketCAN support. For SocketCAN to work with Intrepid devices you will need to have this kernel object loaded on your system. Once the module is built and loaded run [icsscand](https://github.com/intrepidcs/icsscand) to turn on SocketCAN support.
|
||||||
|
|
||||||
First, install the necessary dependencies for building kernel modules.
|
First, install the necessary dependencies for building kernel modules:
|
||||||
|
- Ubuntu: `sudo apt install linux-headers-generic build-essential gcc git`
|
||||||
On Ubuntu, this is acomplished by running `sudo apt install linux-headers-generic build-essential gcc git`
|
- Fedora: `sudo dnf install git kernel-devel-matched`
|
||||||
|
|
||||||
Clone this repository by running `git clone https://github.com/intrepidcs/intrepid-socketcan-kernel-module.git`
|
Clone this repository by running `git clone https://github.com/intrepidcs/intrepid-socketcan-kernel-module.git`
|
||||||
|
|
||||||
|
|
@ -45,3 +45,50 @@ can_raw
|
||||||
can_dev
|
can_dev
|
||||||
intrepid
|
intrepid
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Dynamic Debug Support
|
||||||
|
|
||||||
|
The module includes debug messages that can be enabled at runtime using the kernel's dynamic debug framework. This requires your kernel to be built with `CONFIG_DYNAMIC_DEBUG=y` (most modern distributions include this).
|
||||||
|
|
||||||
|
### Enabling Debug Messages
|
||||||
|
|
||||||
|
After building and loading the module with the standard `make` and `make install`, you can enable debug output:
|
||||||
|
|
||||||
|
**Enable all debug messages for the intrepid module:**
|
||||||
|
```bash
|
||||||
|
$ echo "module intrepid +p" | sudo tee /sys/kernel/debug/dynamic_debug/control
|
||||||
|
```
|
||||||
|
|
||||||
|
**Disable debug messages:**
|
||||||
|
```bash
|
||||||
|
$ echo "module intrepid -p" | sudo tee /sys/kernel/debug/dynamic_debug/control
|
||||||
|
```
|
||||||
|
|
||||||
|
**Enable debug messages for specific functions:**
|
||||||
|
```bash
|
||||||
|
$ echo "file intrepid.c func function_name +p" | sudo tee /sys/kernel/debug/dynamic_debug/control
|
||||||
|
```
|
||||||
|
|
||||||
|
**View current debug settings:**
|
||||||
|
```bash
|
||||||
|
$ sudo cat /sys/kernel/debug/dynamic_debug/control | grep intrepid
|
||||||
|
```
|
||||||
|
|
||||||
|
**View debug output:**
|
||||||
|
```bash
|
||||||
|
$ sudo dmesg | grep intrepid
|
||||||
|
$ sudo dmesg -w # Follow live output
|
||||||
|
```
|
||||||
|
|
||||||
|
### Available Debug Messages
|
||||||
|
|
||||||
|
The debug messages provide information about:
|
||||||
|
- CAN bittiming configuration
|
||||||
|
- Frame validation and processing
|
||||||
|
- Message dropping conditions
|
||||||
|
- Error handling
|
||||||
|
|
||||||
|
Debug messages are primarily triggered during:
|
||||||
|
- CAN interface configuration
|
||||||
|
- Frame transmission/reception
|
||||||
|
- Error conditions and message drops
|
||||||
|
|
|
||||||
655
intrepid.c
655
intrepid.c
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* intrepid.c - Netdevice driver for Intrepid CAN/Ethernet devices
|
* intrepid.c - Netdevice driver for Intrepid CAN/Ethernet devices
|
||||||
*
|
*
|
||||||
* Copyright (c) 2016-2019 Intrepid Control Systems, Inc.
|
* Copyright (c) 2016-2025 Intrepid Control Systems, Inc.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
|
@ -48,6 +48,7 @@
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <linux/poll.h>
|
#include <linux/poll.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
|
#include <linux/etherdevice.h>
|
||||||
|
|
||||||
#include "neomessage.h"
|
#include "neomessage.h"
|
||||||
|
|
||||||
|
|
@ -55,9 +56,9 @@
|
||||||
#define str(x) str_first(x)
|
#define str(x) str_first(x)
|
||||||
|
|
||||||
#define KO_DESC "Netdevice driver for Intrepid CAN/Ethernet devices"
|
#define KO_DESC "Netdevice driver for Intrepid CAN/Ethernet devices"
|
||||||
#define KO_MAJOR 2
|
#define KO_MAJOR 3
|
||||||
#define KO_MINOR 0
|
#define KO_MINOR 1
|
||||||
#define KO_PATCH 4
|
#define KO_PATCH 2
|
||||||
#define KO_VERSION str(KO_MAJOR) "." str(KO_MINOR) "." str(KO_PATCH)
|
#define KO_VERSION str(KO_MAJOR) "." str(KO_MINOR) "." str(KO_PATCH)
|
||||||
#define KO_VERSION_INT (KO_MAJOR << 16) | (KO_MINOR << 8) | KO_PATCH
|
#define KO_VERSION_INT (KO_MAJOR << 16) | (KO_MINOR << 8) | KO_PATCH
|
||||||
|
|
||||||
|
|
@ -69,30 +70,35 @@ MODULE_DESCRIPTION(KO_DESC);
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_AUTHOR("Paul Hollinsky <phollinsky@intrepidcs.com>");
|
MODULE_AUTHOR("Paul Hollinsky <phollinsky@intrepidcs.com>");
|
||||||
MODULE_AUTHOR("Jeffrey Quesnelle <jeffq@intrepidcs.com>");
|
MODULE_AUTHOR("Jeffrey Quesnelle <jeffq@intrepidcs.com>");
|
||||||
|
MODULE_AUTHOR("Kyle Schwarz <kschwarz@intrepidcs.com>");
|
||||||
|
MODULE_AUTHOR("Thomas Stoddard <tstoddard@intrepidcs.com>");
|
||||||
MODULE_VERSION(KO_VERSION);
|
MODULE_VERSION(KO_VERSION);
|
||||||
|
|
||||||
#define INTREPID_DEVICE_NAME "intrepid_netdevice"
|
#define INTREPID_DEVICE_NAME "intrepid_netdevice"
|
||||||
#define INTREPID_CLASS_NAME "intrepid"
|
#define INTREPID_CLASS_NAME "intrepid"
|
||||||
#define MAX_NET_DEVICES 16
|
#define MAX_NET_DEVICES 64
|
||||||
#define SHARED_MEM_SIZE 0x400000
|
#define SHARED_MEM_SIZE 0x400000
|
||||||
|
|
||||||
#define SIOCSADDIF 0x3001
|
#define SIOCSADDCANIF 0x3001
|
||||||
#define SIOCSREMOVEIF 0x3002
|
#define SIOCSADDETHIF 0x3002
|
||||||
#define SIOCGSHAREDMEMSIZE 0x3003
|
#define SIOCSREMOVECANIF 0x3003
|
||||||
#define SIOCSMSGSWRITTEN 0x3004
|
#define SIOCSREMOVEETHIF 0x3004
|
||||||
#define SIOCGMAXIFACES 0x3005
|
#define SIOCGSHAREDMEMSIZE 0x3005
|
||||||
#define SIOCGVERSION 0x3006
|
#define SIOCSMSGSWRITTEN 0x3006
|
||||||
#define SIOCGCLIENTVEROK 0x3007
|
#define SIOCGMAXIFACES 0x3007
|
||||||
|
#define SIOCGVERSION 0x3008
|
||||||
|
#define SIOCGCLIENTVEROK 0x3009
|
||||||
|
#define SIOCSBAUDRATE 0x300A
|
||||||
|
|
||||||
/* This is true until we have Ethernet support
|
/* This is true until we have Ethernet support
|
||||||
* It is used to stop the netif queues before we have to return NETDEV_TX_BUSY
|
* It is used to stop the netif queues before we have to return NETDEV_TX_BUSY
|
||||||
*/
|
*/
|
||||||
#define MAX_MTU CANFD_MTU
|
|
||||||
|
|
||||||
#define KERNEL_CHECKS_MTU_RANGE (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
|
#define KERNEL_CHECKS_MTU_RANGE (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
|
||||||
#define KERNEL_FAULT_TAKES_VMA (LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0))
|
#define KERNEL_FAULT_TAKES_VMA (LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0))
|
||||||
#define KERNEL_SUPPORTS_ALIASES (LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0))
|
#define KERNEL_SUPPORTS_ALIASES (LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0))
|
||||||
#define KERNEL_DEFINES_VM_FAULT_T (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0))
|
#define KERNEL_DEFINES_VM_FAULT_T (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0))
|
||||||
|
#define KERNEL_CAN_ECHO_TRACKS_LEN (LINUX_VERSION_CODE >= KERNEL_VERSION(5,12,0))
|
||||||
|
|
||||||
#if KERNEL_DEFINES_VM_FAULT_T == 0
|
#if KERNEL_DEFINES_VM_FAULT_T == 0
|
||||||
typedef int vm_fault_t;
|
typedef int vm_fault_t;
|
||||||
|
|
@ -106,15 +112,17 @@ struct intrepid_pending_tx_info {
|
||||||
|
|
||||||
struct intrepid_netdevice {
|
struct intrepid_netdevice {
|
||||||
struct can_priv can;
|
struct can_priv can;
|
||||||
struct can_berr_counter bec;
|
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
int is_stopped;
|
int is_stopped;
|
||||||
unsigned char *from_user;
|
unsigned char *from_user;
|
||||||
|
uint8_t tx_idx;
|
||||||
|
int bitrate_changed;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int is_open;
|
static int is_open;
|
||||||
static int major_number;
|
static int major_number;
|
||||||
|
static int client_version;
|
||||||
static unsigned char *shared_mem;
|
static unsigned char *shared_mem;
|
||||||
static struct class *intrepid_dev_class;
|
static struct class *intrepid_dev_class;
|
||||||
static struct device *intrepid_dev;
|
static struct device *intrepid_dev;
|
||||||
|
|
@ -134,6 +142,27 @@ static spinlock_t tx_box_lock;
|
||||||
(shared_mem + (RX_BOX_SIZE * DEVICE_INDEX))
|
(shared_mem + (RX_BOX_SIZE * DEVICE_INDEX))
|
||||||
#define GET_TX_BOX(BOX_INDEX) \
|
#define GET_TX_BOX(BOX_INDEX) \
|
||||||
(shared_mem + (SHARED_MEM_SIZE / 2) + (BOX_INDEX * TX_BOX_SIZE))
|
(shared_mem + (SHARED_MEM_SIZE / 2) + (BOX_INDEX * TX_BOX_SIZE))
|
||||||
|
#define MAX_TX (0x100)
|
||||||
|
#define DESC_OFFSET (2)
|
||||||
|
|
||||||
|
static uint16_t intrepid_next_tx_description(
|
||||||
|
struct intrepid_netdevice* ics,
|
||||||
|
int* idx_out)
|
||||||
|
{
|
||||||
|
/* we offset the description so that we know 0 is not us transmitting */
|
||||||
|
uint16_t description = ics->tx_idx + DESC_OFFSET;
|
||||||
|
*idx_out = ics->tx_idx;
|
||||||
|
ics->tx_idx++;
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intrepid_description_to_idx(uint16_t description)
|
||||||
|
{
|
||||||
|
if (description < DESC_OFFSET || description >= DESC_OFFSET + MAX_TX)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return description - DESC_OFFSET;
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns 1 when we would not have enough space to hold another message of `size` */
|
/* Returns 1 when we would not have enough space to hold another message of `size` */
|
||||||
static inline int intrepid_tx_box_no_space_for(size_t size)
|
static inline int intrepid_tx_box_no_space_for(size_t size)
|
||||||
|
|
@ -144,7 +173,7 @@ static inline int intrepid_tx_box_no_space_for(size_t size)
|
||||||
static void intrepid_unpause_all_queues(void)
|
static void intrepid_unpause_all_queues(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for(i = 0; i < MAX_NET_DEVICES; ++i) {
|
for (i = 0; i < MAX_NET_DEVICES; ++i) {
|
||||||
struct net_device *dev = net_devices[i];
|
struct net_device *dev = net_devices[i];
|
||||||
struct intrepid_netdevice *ics;
|
struct intrepid_netdevice *ics;
|
||||||
if (dev == NULL)
|
if (dev == NULL)
|
||||||
|
|
@ -163,7 +192,7 @@ static void intrepid_unpause_all_queues(void)
|
||||||
static void intrepid_pause_all_queues(void)
|
static void intrepid_pause_all_queues(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for(i = 0; i < MAX_NET_DEVICES; ++i) {
|
for (i = 0; i < MAX_NET_DEVICES; ++i) {
|
||||||
struct net_device *dev = net_devices[i];
|
struct net_device *dev = net_devices[i];
|
||||||
struct intrepid_netdevice *ics;
|
struct intrepid_netdevice *ics;
|
||||||
if (dev == NULL)
|
if (dev == NULL)
|
||||||
|
|
@ -179,21 +208,19 @@ static void intrepid_pause_all_queues(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static netdev_tx_t intrepid_netdevice_xmit(struct sk_buff *skb, struct net_device *dev)
|
static netdev_tx_t intrepid_CAN_netdevice_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
{
|
{
|
||||||
int ret = NETDEV_TX_OK;
|
int ret = NETDEV_TX_OK;
|
||||||
struct net_device_stats *stats = &dev->stats;
|
|
||||||
struct intrepid_netdevice *ics = netdev_priv(dev);
|
struct intrepid_netdevice *ics = netdev_priv(dev);
|
||||||
struct canfd_frame *cf = (struct canfd_frame*)skb->data;
|
struct canfd_frame *cf = (struct canfd_frame*)skb->data;
|
||||||
bool fd = can_is_canfd_skb(skb);
|
bool fd = can_is_canfd_skb(skb);
|
||||||
bool needs_unlock = false;
|
bool needs_unlock = false;
|
||||||
|
bool consumed = false;
|
||||||
|
int tx_idx;
|
||||||
neomessage_can_t msg = {0};
|
neomessage_can_t msg = {0};
|
||||||
|
|
||||||
stats->tx_packets++;
|
|
||||||
stats->tx_bytes = cf->len;
|
|
||||||
|
|
||||||
if (can_dropped_invalid_skb(dev, skb)) {
|
if (can_dropped_invalid_skb(dev, skb)) {
|
||||||
pr_info("intrepid: dropping invalid frame on %s\n", dev->name);
|
pr_debug("intrepid: dropping invalid frame on %s\n", dev->name);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,7 +229,7 @@ static netdev_tx_t intrepid_netdevice_xmit(struct sk_buff *skb, struct net_devic
|
||||||
|
|
||||||
if (unlikely(ics->is_stopped)) {
|
if (unlikely(ics->is_stopped)) {
|
||||||
pr_err("intrepid: in xmit but device is stopped\n");
|
pr_err("intrepid: in xmit but device is stopped\n");
|
||||||
if (intrepid_tx_box_no_space_for(sizeof(neomessage_can_t) + MAX_MTU)) {
|
if (intrepid_tx_box_no_space_for(sizeof(neomessage_can_t) + CANFD_MTU)) {
|
||||||
ret = NETDEV_TX_BUSY;
|
ret = NETDEV_TX_BUSY;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
@ -231,12 +258,12 @@ static netdev_tx_t intrepid_netdevice_xmit(struct sk_buff *skb, struct net_devic
|
||||||
pr_info("intrepid: tried to send RTR frame on CANFD %s\n", dev->name);
|
pr_info("intrepid: tried to send RTR frame on CANFD %s\n", dev->name);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.status.remoteFrame = true;
|
msg.status.remoteFrame = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.length = cf->len;
|
msg.length = cf->len;
|
||||||
msg.netid = dev->base_addr;
|
msg.netid = dev->base_addr;
|
||||||
|
msg.type = ICSNEO_NETWORK_TYPE_CAN;
|
||||||
|
|
||||||
if (intrepid_tx_box_no_space_for(sizeof(neomessage_can_t) + msg.length)) {
|
if (intrepid_tx_box_no_space_for(sizeof(neomessage_can_t) + msg.length)) {
|
||||||
/* This should never happen, the queue should be paused before this */
|
/* This should never happen, the queue should be paused before this */
|
||||||
|
|
@ -246,6 +273,13 @@ static netdev_tx_t intrepid_netdevice_xmit(struct sk_buff *skb, struct net_devic
|
||||||
ret = NETDEV_TX_BUSY;
|
ret = NETDEV_TX_BUSY;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
msg.description = intrepid_next_tx_description(ics, &tx_idx);
|
||||||
|
can_put_echo_skb(skb, dev, tx_idx
|
||||||
|
#if KERNEL_CAN_ECHO_TRACKS_LEN
|
||||||
|
, msg.length
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
consumed = true;
|
||||||
|
|
||||||
/* Copy the message into the usermode box */
|
/* Copy the message into the usermode box */
|
||||||
memcpy(tx_boxes[current_tx_box] + tx_box_bytes[current_tx_box], &msg, sizeof(neomessage_can_t));
|
memcpy(tx_boxes[current_tx_box] + tx_box_bytes[current_tx_box], &msg, sizeof(neomessage_can_t));
|
||||||
|
|
@ -255,14 +289,68 @@ static netdev_tx_t intrepid_netdevice_xmit(struct sk_buff *skb, struct net_devic
|
||||||
tx_box_count[current_tx_box]++;
|
tx_box_count[current_tx_box]++;
|
||||||
|
|
||||||
/* If we might not be able to fit the next message, let's lock until we can to prevent NETDEV_TX_BUSY */
|
/* If we might not be able to fit the next message, let's lock until we can to prevent NETDEV_TX_BUSY */
|
||||||
if (intrepid_tx_box_no_space_for(sizeof(neomessage_can_t) + MAX_MTU))
|
if (intrepid_tx_box_no_space_for(sizeof(neomessage_can_t) + CANFD_MTU))
|
||||||
intrepid_pause_all_queues();
|
intrepid_pause_all_queues();
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
if(ret == NETDEV_TX_OK)
|
if (ret == NETDEV_TX_OK && !consumed)
|
||||||
consume_skb(skb);
|
consume_skb(skb);
|
||||||
wake_up_interruptible(&tx_wait);
|
wake_up_interruptible(&tx_wait);
|
||||||
if(needs_unlock)
|
if (needs_unlock)
|
||||||
|
spin_unlock_bh(&tx_box_lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static netdev_tx_t intrepid_ETH_netdevice_xmit(struct sk_buff *skb, struct net_device *dev) {
|
||||||
|
int ret = NETDEV_TX_OK;
|
||||||
|
struct intrepid_netdevice *ics = netdev_priv(dev);
|
||||||
|
bool needs_unlock = false;
|
||||||
|
bool consumed = false;
|
||||||
|
int tx_idx;
|
||||||
|
neomessage_eth_t msg = {0};
|
||||||
|
|
||||||
|
spin_lock_bh(&tx_box_lock);
|
||||||
|
needs_unlock = true;
|
||||||
|
|
||||||
|
if (unlikely(ics->is_stopped)) {
|
||||||
|
pr_err("intrepid: in xmit but device is stopped\n");
|
||||||
|
if (intrepid_tx_box_no_space_for(sizeof(neomessage_eth_t) + ETH_DATA_LEN)) {
|
||||||
|
ret = NETDEV_TX_BUSY;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
pr_warn("intrepid: device should not have been stopped, waking all\n");
|
||||||
|
intrepid_unpause_all_queues();
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.length = skb->len;
|
||||||
|
msg.netid = dev->base_addr;
|
||||||
|
msg.type = ICSNEO_NETWORK_TYPE_ETHERNET;
|
||||||
|
|
||||||
|
if (intrepid_tx_box_no_space_for(sizeof(neomessage_eth_t) + msg.length)) {
|
||||||
|
/* This should never happen, the queue should be paused before this */
|
||||||
|
ssize_t offset = TX_BOX_SIZE;
|
||||||
|
offset -= (tx_box_bytes[current_tx_box] + sizeof(neomessage_eth_t) + msg.length);
|
||||||
|
pr_err("intrepid: %zu length message caused NETDEV_TX_BUSY (%zd)\n", msg.length, offset);
|
||||||
|
ret = NETDEV_TX_BUSY;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
msg.description = intrepid_next_tx_description(ics, &tx_idx);
|
||||||
|
consumed = true;
|
||||||
|
|
||||||
|
/* Copy the message into the usermode box */
|
||||||
|
memcpy(tx_boxes[current_tx_box] + tx_box_bytes[current_tx_box], &msg, sizeof(neomessage_eth_t));
|
||||||
|
tx_box_bytes[current_tx_box] += sizeof(neomessage_eth_t);
|
||||||
|
memcpy(tx_boxes[current_tx_box] + tx_box_bytes[current_tx_box], skb->data, skb->len);
|
||||||
|
tx_box_bytes[current_tx_box] += msg.length;
|
||||||
|
tx_box_count[current_tx_box]++;
|
||||||
|
|
||||||
|
/* If we might not be able to fit the next message, let's lock until we can to prevent NETDEV_TX_BUSY */
|
||||||
|
if (intrepid_tx_box_no_space_for(sizeof(neomessage_eth_t) + ETH_DATA_LEN))
|
||||||
|
intrepid_pause_all_queues();
|
||||||
|
exit:
|
||||||
|
if (ret == NETDEV_TX_OK && !consumed)
|
||||||
|
consume_skb(skb);
|
||||||
|
wake_up_interruptible(&tx_wait);
|
||||||
|
if (needs_unlock)
|
||||||
spin_unlock_bh(&tx_box_lock);
|
spin_unlock_bh(&tx_box_lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -291,10 +379,19 @@ static int intrepid_netdevice_open(struct net_device *dev)
|
||||||
// return -EINVAL;
|
// return -EINVAL;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
static const struct net_device_ops intrepid_netdevice_ops = {
|
//CAN
|
||||||
|
static const struct net_device_ops intrepid_CAN_netdevice_ops = {
|
||||||
.ndo_open = intrepid_netdevice_open,
|
.ndo_open = intrepid_netdevice_open,
|
||||||
.ndo_stop = intrepid_netdevice_stop,
|
.ndo_stop = intrepid_netdevice_stop,
|
||||||
.ndo_start_xmit = intrepid_netdevice_xmit,
|
.ndo_start_xmit = intrepid_CAN_netdevice_xmit,
|
||||||
|
//.ndo_change_mtu = intrepid_netdevice_change_mtu,
|
||||||
|
};
|
||||||
|
|
||||||
|
//ETH
|
||||||
|
static const struct net_device_ops intrepid_ETH_netdevice_ops = {
|
||||||
|
.ndo_open = intrepid_netdevice_open,
|
||||||
|
.ndo_stop = intrepid_netdevice_stop,
|
||||||
|
.ndo_start_xmit = intrepid_ETH_netdevice_xmit,
|
||||||
//.ndo_change_mtu = intrepid_netdevice_change_mtu,
|
//.ndo_change_mtu = intrepid_netdevice_change_mtu,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -326,6 +423,68 @@ static int intrepid_remove_can_if(int index)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int intrepid_set_bittiming(struct net_device *netdev)
|
||||||
|
{
|
||||||
|
struct intrepid_netdevice *dev = netdev_priv(netdev);
|
||||||
|
struct can_bittiming *bt = &dev->can.bittiming;
|
||||||
|
|
||||||
|
dev_dbg(&netdev->dev, "bitrate %d sample_point %d tq %d sjw %d phase1 %d phase2 %d prop %d brp %d",
|
||||||
|
bt->bitrate, bt->sample_point, bt->tq, bt->sjw, bt->phase_seg1, bt->phase_seg2, bt->prop_seg, bt->brp);
|
||||||
|
|
||||||
|
dev->bitrate_changed = 1;
|
||||||
|
wake_up_interruptible(&tx_wait);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intrepid_set_data_bittiming(struct net_device *netdev)
|
||||||
|
{
|
||||||
|
struct intrepid_netdevice *dev = netdev_priv(netdev);
|
||||||
|
struct can_bittiming *bt = &dev->can.data_bittiming;
|
||||||
|
|
||||||
|
dev_dbg(&netdev->dev, "bitrate %d sample_point %d tq %d sjw %d phase1 %d phase2 %d prop %d brp %d",
|
||||||
|
bt->bitrate, bt->sample_point, bt->tq, bt->sjw, bt->phase_seg1, bt->phase_seg2, bt->prop_seg, bt->brp);
|
||||||
|
|
||||||
|
dev->bitrate_changed = 1;
|
||||||
|
wake_up_interruptible(&tx_wait);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intrepid_bitrates[] = {
|
||||||
|
20000,
|
||||||
|
33000,
|
||||||
|
50000,
|
||||||
|
62000,
|
||||||
|
83000,
|
||||||
|
100000,
|
||||||
|
125000,
|
||||||
|
250000,
|
||||||
|
500000,
|
||||||
|
666000,
|
||||||
|
800000,
|
||||||
|
1000000
|
||||||
|
};
|
||||||
|
|
||||||
|
static int intrepid_data_bitrates[] = {
|
||||||
|
20000,
|
||||||
|
33000,
|
||||||
|
50000,
|
||||||
|
62000,
|
||||||
|
83000,
|
||||||
|
100000,
|
||||||
|
125000,
|
||||||
|
250000,
|
||||||
|
500000,
|
||||||
|
666000,
|
||||||
|
800000,
|
||||||
|
1000000,
|
||||||
|
2000000,
|
||||||
|
4000000,
|
||||||
|
5000000,
|
||||||
|
6667000,
|
||||||
|
8000000,
|
||||||
|
10000000
|
||||||
|
};
|
||||||
|
|
||||||
static int intrepid_add_can_if(struct intrepid_netdevice **result, const char *requestedName)
|
static int intrepid_add_can_if(struct intrepid_netdevice **result, const char *requestedName)
|
||||||
{
|
{
|
||||||
// The `requestedName` parameter is always NULL if KERNEL_SUPPORTS_ALIASES is false
|
// The `requestedName` parameter is always NULL if KERNEL_SUPPORTS_ALIASES is false
|
||||||
|
|
@ -350,21 +509,20 @@ static int intrepid_add_can_if(struct intrepid_netdevice **result, const char *r
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev = alloc_candev(sizeof(*ics), 1);
|
dev = alloc_candev(sizeof(*ics), MAX_TX);
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
pr_alert("intrepid: Could not allocate candev\n");
|
pr_alert("intrepid: Could not allocate candev\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
dev->base_addr = i;
|
dev->base_addr = i;
|
||||||
dev->flags |= IFF_ECHO;
|
dev->flags |= IFF_ECHO;
|
||||||
#if KERNEL_CHECKS_MTU_RANGE
|
#if KERNEL_CHECKS_MTU_RANGE
|
||||||
dev->min_mtu = CAN_MTU;
|
dev->min_mtu = CAN_MTU;
|
||||||
dev->max_mtu = MAX_MTU;
|
dev->max_mtu = CANFD_MTU;
|
||||||
#endif
|
#endif
|
||||||
dev->mtu = CANFD_MTU; /* TODO: Check CAN-FD support from usermode daemon */
|
dev->mtu = CANFD_MTU; /* TODO: Check CAN-FD support from usermode daemon */
|
||||||
dev->netdev_ops = &intrepid_netdevice_ops;
|
dev->netdev_ops = &intrepid_CAN_netdevice_ops;
|
||||||
#if KERNEL_SUPPORTS_ALIASES
|
#if KERNEL_SUPPORTS_ALIASES
|
||||||
if (requestedName && ((aliasLen = strlen(requestedName)) > 0) && aliasLen < IFALIASZ) {
|
if (requestedName && ((aliasLen = strlen(requestedName)) > 0) && aliasLen < IFALIASZ) {
|
||||||
dev->ifalias = kzalloc(sizeof(struct dev_ifalias) + aliasLen + 1, GFP_KERNEL);
|
dev->ifalias = kzalloc(sizeof(struct dev_ifalias) + aliasLen + 1, GFP_KERNEL);
|
||||||
|
|
@ -380,6 +538,18 @@ static int intrepid_add_can_if(struct intrepid_netdevice **result, const char *r
|
||||||
ics->dev = dev;
|
ics->dev = dev;
|
||||||
ics->is_stopped = 0;
|
ics->is_stopped = 0;
|
||||||
ics->from_user = GET_RX_BOX(i); /* incoming rx messages */
|
ics->from_user = GET_RX_BOX(i); /* incoming rx messages */
|
||||||
|
ics->tx_idx = 0;
|
||||||
|
|
||||||
|
if (VER_MIN_FROM_INT(client_version) > 1) {
|
||||||
|
ics->can.bitrate_const = intrepid_bitrates;
|
||||||
|
ics->can.bitrate_const_cnt = ARRAY_SIZE(intrepid_bitrates);
|
||||||
|
ics->can.data_bitrate_const = intrepid_data_bitrates;
|
||||||
|
ics->can.data_bitrate_const_cnt = ARRAY_SIZE(intrepid_data_bitrates);
|
||||||
|
ics->can.do_set_bittiming = intrepid_set_bittiming;
|
||||||
|
ics->can.do_set_data_bittiming = intrepid_set_data_bittiming;
|
||||||
|
}
|
||||||
|
ics->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||||
|
ics->can.ctrlmode_supported = CAN_CTRLMODE_FD;
|
||||||
|
|
||||||
spin_lock_init(&ics->lock);
|
spin_lock_init(&ics->lock);
|
||||||
|
|
||||||
|
|
@ -471,7 +641,10 @@ static int intrepid_fill_can_frame_from_neomessage(
|
||||||
if (msg->status.remoteFrame)
|
if (msg->status.remoteFrame)
|
||||||
cf->can_id |= CAN_RTR_FLAG;
|
cf->can_id |= CAN_RTR_FLAG;
|
||||||
|
|
||||||
cf->can_dlc = get_can_dlc(msg->length);
|
if (unlikely(msg->length > 8))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
cf->can_dlc = msg->length;
|
||||||
memcpy(cf->data, data, cf->can_dlc);
|
memcpy(cf->data, data, cf->can_dlc);
|
||||||
|
|
||||||
stats->rx_bytes += cf->can_dlc;
|
stats->rx_bytes += cf->can_dlc;
|
||||||
|
|
@ -480,68 +653,291 @@ static int intrepid_fill_can_frame_from_neomessage(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns true if this message was handled as a transmit receipt.
|
||||||
|
* If false is returned, this message should be handled as a receive
|
||||||
|
* message, regardless of the transmit flag.
|
||||||
|
*/
|
||||||
|
static bool handle_CAN_transmit_receipt(
|
||||||
|
struct net_device *device,
|
||||||
|
const neomessage_can_t *msg,
|
||||||
|
const uint8_t *data,
|
||||||
|
struct net_device_stats *stats)
|
||||||
|
{
|
||||||
|
int length;
|
||||||
|
int tx_idx;
|
||||||
|
|
||||||
|
if (!msg->status.transmitMessage)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
tx_idx = intrepid_description_to_idx(msg->description);
|
||||||
|
|
||||||
|
/* not transmitted by us, maybe by CoreMini */
|
||||||
|
/* just handle it as a receive */
|
||||||
|
if (tx_idx < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* unsuccessful transmits */
|
||||||
|
/* stats are handled in intrepid_fill_canerr_frame_from_neomessage */
|
||||||
|
if (msg->status.globalError) {
|
||||||
|
can_free_echo_skb(device, tx_idx
|
||||||
|
#if KERNEL_CAN_ECHO_TRACKS_LEN
|
||||||
|
, NULL
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
length = can_get_echo_skb(device, tx_idx
|
||||||
|
#if KERNEL_CAN_ECHO_TRACKS_LEN
|
||||||
|
, NULL
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
stats->tx_packets++;
|
||||||
|
stats->tx_bytes += length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool handle_ETH_transmit_receipt(
|
||||||
|
const neomessage_eth_t *msg,
|
||||||
|
const uint8_t *data,
|
||||||
|
struct net_device_stats *stats)
|
||||||
|
{
|
||||||
|
int tx_idx;
|
||||||
|
|
||||||
|
if (!msg->status.transmitMessage) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tx_idx = intrepid_description_to_idx(msg->description);
|
||||||
|
|
||||||
|
/* not transmitted by us, maybe by CoreMini */
|
||||||
|
/* just handle it as a receive */
|
||||||
|
if (tx_idx < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* unsuccessful transmits */
|
||||||
|
if (msg->status.globalError) {
|
||||||
|
struct sk_buff *skb;
|
||||||
|
kfree_skb(&skb[tx_idx]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stats->tx_packets++;
|
||||||
|
stats->tx_bytes += msg->length;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intrepid_remove_eth_if(int index)
|
||||||
|
{
|
||||||
|
struct net_device *device = intrepid_get_dev_by_index(index);
|
||||||
|
if (!device)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (index != device->base_addr)
|
||||||
|
pr_warn("intrepid: Index of device %ld does not match given index %d\n", device->base_addr, index);
|
||||||
|
|
||||||
|
pr_info("intrepid: Removing device %d %s 0x%p\n", index, device->name, device);
|
||||||
|
|
||||||
|
unregister_netdev(device);
|
||||||
|
|
||||||
|
net_devices[index] = NULL;
|
||||||
|
|
||||||
|
pr_info("intrepid: Removed device %d\n", index);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intrepid_add_eth_if(struct intrepid_netdevice **result, const char *requestedName)
|
||||||
|
{
|
||||||
|
// The `requestedName` parameter is always NULL if KERNEL_SUPPORTS_ALIASES is false
|
||||||
|
#if KERNEL_SUPPORTS_ALIASES
|
||||||
|
size_t aliasLen = 0;
|
||||||
|
#endif
|
||||||
|
int i = 0;
|
||||||
|
int ret = -EPERM;
|
||||||
|
struct net_device *dev = NULL;
|
||||||
|
struct intrepid_netdevice *ics = NULL;
|
||||||
|
|
||||||
|
*result = NULL;
|
||||||
|
// Find the first NULL position in net_devices
|
||||||
|
for (i = 0; i < MAX_NET_DEVICES; ++i) {
|
||||||
|
if (net_devices[i] == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If for loop never broke, there is no room in net_devices
|
||||||
|
if (i == MAX_NET_DEVICES) {
|
||||||
|
pr_alert("intrepid: No more netdevices available\n");
|
||||||
|
ret = -ENFILE;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocates and sets an ethernet device
|
||||||
|
dev = alloc_etherdev(sizeof(*ics));
|
||||||
|
|
||||||
|
if (!dev) {
|
||||||
|
pr_alert("intrepid: Could not allocate ethernet device\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->base_addr = i;
|
||||||
|
dev->flags |= IFF_BROADCAST | IFF_MULTICAST | IFF_RUNNING;
|
||||||
|
dev->operstate = IF_OPER_UP;
|
||||||
|
#if KERNEL_CHECKS_MTU_RANGE
|
||||||
|
dev->min_mtu = ETH_MIN_MTU;
|
||||||
|
dev->max_mtu = ETH_MAX_MTU;
|
||||||
|
#endif
|
||||||
|
dev->mtu = ETH_DATA_LEN;
|
||||||
|
dev->netdev_ops = &intrepid_ETH_netdevice_ops;
|
||||||
|
#if KERNEL_SUPPORTS_ALIASES
|
||||||
|
if (requestedName && ((aliasLen = strlen(requestedName)) > 0) && aliasLen < IFALIASZ) {
|
||||||
|
dev->ifalias = kzalloc(sizeof(struct dev_ifalias) + aliasLen + 1, GFP_KERNEL);
|
||||||
|
if (dev->ifalias == NULL) {
|
||||||
|
pr_alert("inrepid: Could not allocate space for ifalias %zu\n", sizeof(struct dev_ifalias));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
strncpy(dev->ifalias->ifalias, requestedName, aliasLen + 1);
|
||||||
|
pr_info("intrepid: %s alias set to %s\n", dev->name, requestedName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
eth_hw_addr_random(dev);
|
||||||
|
|
||||||
|
ics = netdev_priv(dev);
|
||||||
|
ics->dev = dev;
|
||||||
|
ics->is_stopped = 0;
|
||||||
|
ics->from_user = GET_RX_BOX(i);
|
||||||
|
ics->tx_idx = 0;
|
||||||
|
|
||||||
|
spin_lock_init(&ics->lock);
|
||||||
|
|
||||||
|
ret = register_netdev(dev);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
pr_alert("intrepid: Could not register ethernet device\n");
|
||||||
|
free_netdev(dev);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
net_devices[i] = dev;
|
||||||
|
*result = ics;
|
||||||
|
|
||||||
|
ret = i;
|
||||||
|
|
||||||
|
pr_info("intrepid: Allocated new netdevice %s @ %d\n", dev->name, ret);
|
||||||
|
exit:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intrepid_fill_eth_frame_from_neomessage(
|
||||||
|
struct net_device_stats *stats,
|
||||||
|
const neomessage_eth_t *msg,
|
||||||
|
const uint8_t *data,
|
||||||
|
struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
if (unlikely(msg->length > (ETH_FRAME_LEN + ETH_FCS_LEN))) {
|
||||||
|
stats->rx_dropped++;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(skb == NULL)) {
|
||||||
|
stats->rx_dropped++;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
skb_put_data(skb, data, msg->length);
|
||||||
|
skb->protocol = eth_type_trans(skb, skb->dev);
|
||||||
|
stats->rx_bytes += msg->length;
|
||||||
|
stats->rx_packets++;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct sk_buff *intrepid_skb_from_neomessage(
|
static struct sk_buff *intrepid_skb_from_neomessage(
|
||||||
struct net_device *device,
|
struct net_device *device,
|
||||||
const neomessage_t *msg,
|
const neomessage_frame_t *msg_generic,
|
||||||
const uint8_t *data,
|
const uint8_t *data,
|
||||||
struct net_device_stats *stats)
|
struct net_device_stats *stats)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb = NULL;
|
struct sk_buff *skb = NULL;
|
||||||
struct canfd_frame* cf = NULL;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* input validation */
|
/* input validation */
|
||||||
if (unlikely(device == NULL || msg == NULL || data == NULL || stats == NULL)) {
|
if (unlikely(device == NULL || msg_generic == NULL || data == NULL || stats == NULL)) {
|
||||||
stats->rx_dropped++;
|
stats->rx_dropped++;
|
||||||
pr_warn("intrepid: Dropping message on %s, skb from neomessage input validation failed", device->name);
|
pr_debug("intrepid: Dropping message on %s, skb from neomessage input validation failed", device->name);
|
||||||
goto fail;
|
goto out;
|
||||||
}
|
}
|
||||||
|
switch (msg_generic->type) {
|
||||||
if (msg->status.globalError)
|
case ICSNEO_NETWORK_TYPE_CAN:
|
||||||
|
{
|
||||||
|
struct canfd_frame* cf = NULL;
|
||||||
|
const neomessage_can_t* can_msg = NULL;
|
||||||
|
can_msg = (const neomessage_can_t*)msg_generic;
|
||||||
|
if (handle_CAN_transmit_receipt(device, can_msg, data, stats))
|
||||||
|
goto out;
|
||||||
|
if (can_msg->status.globalError)
|
||||||
skb = alloc_can_err_skb(device, (struct can_frame**)&cf);
|
skb = alloc_can_err_skb(device, (struct can_frame**)&cf);
|
||||||
else if (msg->status.canfdFDF)
|
else if (can_msg->status.canfdFDF)
|
||||||
skb = alloc_canfd_skb(device, &cf);
|
skb = alloc_canfd_skb(device, &cf);
|
||||||
else
|
else
|
||||||
skb = alloc_can_skb(device, (struct can_frame**)&cf);
|
skb = alloc_can_skb(device, (struct can_frame**)&cf);
|
||||||
|
|
||||||
|
/* if this message is handled as a transmit receipt,
|
||||||
|
* don't turn it into a receive skb here.
|
||||||
|
*/
|
||||||
if (unlikely(skb == NULL)) {
|
if (unlikely(skb == NULL)) {
|
||||||
stats->rx_dropped++;
|
stats->rx_dropped++;
|
||||||
pr_warn("intrepid: Dropping message on %s, skb allocation failed", device->name);
|
pr_warn("intrepid: Dropping message on %s, skb allocation failed", device->name);
|
||||||
goto fail;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(msg->type) {
|
if (can_msg->status.globalError)
|
||||||
case ICSNEO_NETWORK_TYPE_CAN:
|
|
||||||
if (msg->status.globalError)
|
|
||||||
ret = intrepid_fill_canerr_frame_from_neomessage(
|
ret = intrepid_fill_canerr_frame_from_neomessage(
|
||||||
stats,
|
stats,
|
||||||
(struct can_frame*)cf,
|
(struct can_frame*)cf,
|
||||||
(const neomessage_can_t*)msg);
|
(const neomessage_can_t*)can_msg);
|
||||||
else if (msg->status.canfdFDF)
|
else if (can_msg->status.canfdFDF)
|
||||||
ret = intrepid_fill_canfd_frame_from_neomessage(
|
ret = intrepid_fill_canfd_frame_from_neomessage(
|
||||||
stats,
|
stats,
|
||||||
cf,
|
cf,
|
||||||
(const neomessage_can_t*)msg,
|
(const neomessage_can_t*)can_msg,
|
||||||
data);
|
data);
|
||||||
else
|
else
|
||||||
ret = intrepid_fill_can_frame_from_neomessage(
|
ret = intrepid_fill_can_frame_from_neomessage(
|
||||||
stats,
|
stats,
|
||||||
(struct can_frame*)cf,
|
(struct can_frame*)cf,
|
||||||
(const neomessage_can_t*)msg,
|
(const neomessage_can_t*)can_msg,
|
||||||
data);
|
data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ICSNEO_NETWORK_TYPE_ETHERNET:
|
||||||
|
{
|
||||||
|
const neomessage_eth_t *msg = (const neomessage_eth_t*)msg_generic;
|
||||||
|
if (handle_ETH_transmit_receipt(msg, data, stats))
|
||||||
|
goto out;
|
||||||
|
skb = netdev_alloc_skb_ip_align(device, msg->length);
|
||||||
|
if (unlikely(skb == NULL)) {
|
||||||
|
stats->rx_dropped++;
|
||||||
|
pr_warn("intrepid: Dropping message on %s, skb allocation failed", device->name);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = intrepid_fill_eth_frame_from_neomessage(stats, msg, data, skb);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pr_warn("intrepid: Dropping message on %s, invalid type %d", device->name, msg->type);
|
pr_debug("intrepid: Dropping message on %s, invalid type %d", device->name, msg_generic->type);
|
||||||
goto fail;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (unlikely(ret != 0)) {
|
if (unlikely(ret != 0)) {
|
||||||
pr_warn("intrepid: Dropping message on %s, frame fill failed", device->name);
|
pr_debug("intrepid: Dropping message on %s, frame fill failed", device->name);
|
||||||
goto fail;
|
goto out;
|
||||||
}
|
}
|
||||||
|
out:
|
||||||
fail:
|
|
||||||
return skb;
|
return skb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -566,23 +962,23 @@ static int intrepid_read_messages(int device_index, unsigned int count)
|
||||||
* converting neomessage_t to a CAN sk_buff */
|
* converting neomessage_t to a CAN sk_buff */
|
||||||
|
|
||||||
while (count--) {
|
while (count--) {
|
||||||
const neomessage_t *msg;
|
const neomessage_frame_t *msg;
|
||||||
const uint8_t *data;
|
const uint8_t *data;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
msg = (const neomessage_t*)currentPosition;
|
msg = (const neomessage_frame_t*)currentPosition;
|
||||||
currentPosition += sizeof(neomessage_t);
|
currentPosition += sizeof(neomessage_frame_t);
|
||||||
data = currentPosition;
|
data = currentPosition;
|
||||||
currentPosition += msg->length;
|
currentPosition += msg->length;
|
||||||
|
|
||||||
/* pass along the converted message to the kernel for dispatch */
|
|
||||||
skb = intrepid_skb_from_neomessage(device, msg, data, stats);
|
skb = intrepid_skb_from_neomessage(device, msg, data, stats);
|
||||||
if (likely(skb != NULL))
|
/* pass along the converted message to the kernel for dispatch */
|
||||||
|
if (skb != NULL)
|
||||||
ret = netif_rx(skb);
|
ret = netif_rx(skb);
|
||||||
|
|
||||||
if (ret == NET_RX_DROP)
|
if (ret == NET_RX_DROP)
|
||||||
pr_warn("intrepid: Dropping message on %s, dropped by kernel", device->name);
|
pr_debug("intrepid: Dropping message on %s, dropped by kernel", device->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_bh(&ics->lock);
|
spin_unlock_bh(&ics->lock);
|
||||||
|
|
@ -597,8 +993,8 @@ static long intrepid_dev_ioctl(struct file *fp, unsigned int cmd, unsigned long
|
||||||
return -ERESTARTSYS;
|
return -ERESTARTSYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (cmd) {
|
switch(cmd) {
|
||||||
case SIOCSADDIF: {
|
case SIOCSADDCANIF: {
|
||||||
struct intrepid_netdevice *result = NULL;
|
struct intrepid_netdevice *result = NULL;
|
||||||
#if KERNEL_SUPPORTS_ALIASES
|
#if KERNEL_SUPPORTS_ALIASES
|
||||||
char requestedNameBuffer[IFALIASZ] = {0};
|
char requestedNameBuffer[IFALIASZ] = {0};
|
||||||
|
|
@ -606,7 +1002,7 @@ static long intrepid_dev_ioctl(struct file *fp, unsigned int cmd, unsigned long
|
||||||
int bytesNotCopied = 0;
|
int bytesNotCopied = 0;
|
||||||
if ((void __user*)arg != NULL) {
|
if ((void __user*)arg != NULL) {
|
||||||
bytesNotCopied = copy_from_user(requestedNameBuffer, (void __user*)arg, IFALIASZ);
|
bytesNotCopied = copy_from_user(requestedNameBuffer, (void __user*)arg, IFALIASZ);
|
||||||
if(bytesNotCopied != 0)
|
if (bytesNotCopied != 0)
|
||||||
pr_warn("intrepid: %d bytes not copied for alias", bytesNotCopied);
|
pr_warn("intrepid: %d bytes not copied for alias", bytesNotCopied);
|
||||||
requestedName = requestedNameBuffer;
|
requestedName = requestedNameBuffer;
|
||||||
}
|
}
|
||||||
|
|
@ -616,9 +1012,46 @@ static long intrepid_dev_ioctl(struct file *fp, unsigned int cmd, unsigned long
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SIOCSREMOVEIF:
|
case SIOCSREMOVECANIF:
|
||||||
ret = intrepid_remove_can_if (arg);
|
ret = intrepid_remove_can_if (arg);
|
||||||
break;
|
break;
|
||||||
|
case SIOCSBAUDRATE: {
|
||||||
|
struct baudrate_info {
|
||||||
|
int handle;
|
||||||
|
int64_t baudrates[2];
|
||||||
|
} info;
|
||||||
|
ret = copy_from_user(&info, (void __user*)arg, sizeof(info));
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
struct net_device *device = intrepid_get_dev_by_index(info.handle);
|
||||||
|
if (device == NULL)
|
||||||
|
break;
|
||||||
|
struct intrepid_netdevice *ics = netdev_priv(device);
|
||||||
|
ics->can.bittiming.bitrate = info.baudrates[0];
|
||||||
|
ics->can.data_bittiming.bitrate = info.baudrates[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SIOCSADDETHIF: {
|
||||||
|
struct intrepid_netdevice *result = NULL;
|
||||||
|
#if KERNEL_SUPPORTS_ALIASES
|
||||||
|
char requestedNameBuffer[IFALIASZ] = {0};
|
||||||
|
char* requestedName = NULL;
|
||||||
|
int bytesNotCopied = 0;
|
||||||
|
if ((void __user*)arg != NULL) {
|
||||||
|
bytesNotCopied = copy_from_user(requestedNameBuffer, (void __user*)arg, IFALIASZ);
|
||||||
|
if (bytesNotCopied != 0)
|
||||||
|
pr_warn("intrepid: %d bytes not copied for alias", bytesNotCopied);
|
||||||
|
requestedName = requestedNameBuffer;
|
||||||
|
}
|
||||||
|
ret = intrepid_add_eth_if(&result, requestedName);
|
||||||
|
#else
|
||||||
|
ret = intrepid_add_eth_if(&result, NULL);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SIOCSREMOVEETHIF:
|
||||||
|
ret = intrepid_remove_eth_if (arg);
|
||||||
|
break;
|
||||||
case SIOCGSHAREDMEMSIZE:
|
case SIOCGSHAREDMEMSIZE:
|
||||||
ret = SHARED_MEM_SIZE;
|
ret = SHARED_MEM_SIZE;
|
||||||
break;
|
break;
|
||||||
|
|
@ -629,13 +1062,8 @@ static long intrepid_dev_ioctl(struct file *fp, unsigned int cmd, unsigned long
|
||||||
ret = KO_VERSION_INT;
|
ret = KO_VERSION_INT;
|
||||||
break;
|
break;
|
||||||
case SIOCGCLIENTVEROK:
|
case SIOCGCLIENTVEROK:
|
||||||
/* Here we can do checks to see if the usermode daemon is
|
client_version = arg;
|
||||||
* a compatible version with us. We don't enforce anything
|
if (VER_MAJ_FROM_INT(arg) == 3 && VER_MIN_FROM_INT(arg) >= 1 && VER_PATCH_FROM_INT(arg) >= 0)
|
||||||
* on the kernel side. icsscand v2.0.0 will not work with
|
|
||||||
* older kernels, and would display an obscure error, thus
|
|
||||||
* we want to ask the user to update to v2.0.1 or later
|
|
||||||
*/
|
|
||||||
if (VER_MAJ_FROM_INT(arg) == 2 && (VER_MIN_FROM_INT(arg) > 0 || VER_PATCH_FROM_INT(arg) >= 1))
|
|
||||||
ret = 0; /* ok to start */
|
ret = 0; /* ok to start */
|
||||||
else
|
else
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
|
@ -700,14 +1128,45 @@ static int intrepid_dev_release(struct inode *ip, struct file *fp)
|
||||||
wake_up_interruptible(&tx_wait);
|
wake_up_interruptible(&tx_wait);
|
||||||
|
|
||||||
for (i = 0; i < MAX_NET_DEVICES; i++) {
|
for (i = 0; i < MAX_NET_DEVICES; i++) {
|
||||||
if (net_devices[i] != NULL)
|
if (net_devices[i] != NULL) {
|
||||||
|
if (net_devices[i]->type == ARPHRD_CAN)
|
||||||
intrepid_remove_can_if(i);
|
intrepid_remove_can_if(i);
|
||||||
|
else if (net_devices[i]->type == ARPHRD_ETHER)
|
||||||
|
intrepid_remove_eth_if(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is_open = 0;
|
is_open = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Re-use the pending_tx_info struct to send changed bitrates to userland
|
||||||
|
* Set the box index to -(dev_id) and encode the rest of the data in the count
|
||||||
|
* and bytes fields.
|
||||||
|
* count is the bitrate value
|
||||||
|
* bytes is the data bitrate value. */
|
||||||
|
static int check_bitrate_change(struct intrepid_pending_tx_info *info)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct intrepid_netdevice *ics;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_NET_DEVICES; i++) {
|
||||||
|
if (net_devices[i] == NULL || net_devices[i]->type != ARPHRD_CAN)
|
||||||
|
continue;
|
||||||
|
ics = netdev_priv(net_devices[i]);
|
||||||
|
|
||||||
|
if (ics->bitrate_changed) {
|
||||||
|
info->tx_box_index = -(i + 1);
|
||||||
|
info->count = ics->can.bittiming.bitrate;
|
||||||
|
info->bytes = ics->can.data_bittiming.bitrate;
|
||||||
|
ics->bitrate_changed = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* usermode uses read() to get the current size of the tx buffer. we use a ping pong buffer
|
/* usermode uses read() to get the current size of the tx buffer. we use a ping pong buffer
|
||||||
* so the user doesn't have to worry about the data changing out from under them while
|
* so the user doesn't have to worry about the data changing out from under them while
|
||||||
* still avoiding a full copy to user. the ping pong flips on every call to this func */
|
* still avoiding a full copy to user. the ping pong flips on every call to this func */
|
||||||
|
|
@ -719,6 +1178,15 @@ static ssize_t intrepid_dev_read(struct file *fp, char *buffer, size_t len, loff
|
||||||
if (len < sizeof(info))
|
if (len < sizeof(info))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
|
/* check if we have to send a bitrate change */
|
||||||
|
if (VER_MIN_FROM_INT(client_version) > 1) {
|
||||||
|
if (check_bitrate_change(&info)) {
|
||||||
|
if (copy_to_user(buffer, &info, sizeof(info)))
|
||||||
|
return -EFAULT;
|
||||||
|
return sizeof(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_bh(&tx_box_lock);
|
spin_lock_bh(&tx_box_lock);
|
||||||
|
|
||||||
/* fill out the info for the user */
|
/* fill out the info for the user */
|
||||||
|
|
@ -753,6 +1221,18 @@ static unsigned int intrepid_dev_poll(struct file *fp, poll_table *wait)
|
||||||
if (tx_box_count[current_tx_box] > 0)
|
if (tx_box_count[current_tx_box] > 0)
|
||||||
return POLLIN | POLLRDNORM;
|
return POLLIN | POLLRDNORM;
|
||||||
|
|
||||||
|
if (VER_MIN_FROM_INT(client_version) > 1) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < MAX_NET_DEVICES; i++) {
|
||||||
|
if (net_devices[i] == NULL || net_devices[i]->type != ARPHRD_CAN)
|
||||||
|
continue;
|
||||||
|
struct intrepid_netdevice *ics = netdev_priv(net_devices[i]);
|
||||||
|
|
||||||
|
if (ics->bitrate_changed)
|
||||||
|
return POLLIN | POLLRDNORM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -770,6 +1250,17 @@ static __init int intrepid_init(void)
|
||||||
int ret;
|
int ret;
|
||||||
pr_info("intrepid: %s %s\n", KO_DESC, KO_VERSION);
|
pr_info("intrepid: %s %s\n", KO_DESC, KO_VERSION);
|
||||||
|
|
||||||
|
BUILD_BUG_ON_MSG(sizeof(neomessage_t) != (56 + sizeof(void*) + sizeof(size_t)),
|
||||||
|
"neomessage_t size is incorrect!");
|
||||||
|
BUILD_BUG_ON_MSG(sizeof(neomessage_frame_t) != sizeof(neomessage_t),
|
||||||
|
"All types of neomessage_t must be the same size! (Base frame is not)");
|
||||||
|
BUILD_BUG_ON_MSG(sizeof(neomessage_can_t) != sizeof(neomessage_t),
|
||||||
|
"All types of neomessage_t must be the same size! (CAN is not)");
|
||||||
|
BUILD_BUG_ON_MSG(sizeof(neomessage_can_error_t) != sizeof(neomessage_t),
|
||||||
|
"All types of neomessage_t must be the same size! (CAN error is not)");
|
||||||
|
BUILD_BUG_ON_MSG(sizeof(neomessage_eth_t) != sizeof(neomessage_t),
|
||||||
|
"All types of neomessage_t must be the same size! (Ethernet is not)");
|
||||||
|
|
||||||
is_open = 0;
|
is_open = 0;
|
||||||
|
|
||||||
/* this is the shared memory used to transfer between us and the user daemon */
|
/* this is the shared memory used to transfer between us and the user daemon */
|
||||||
|
|
@ -811,7 +1302,11 @@ static __init int intrepid_init(void)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
|
||||||
intrepid_dev_class = class_create(THIS_MODULE, INTREPID_CLASS_NAME);
|
intrepid_dev_class = class_create(THIS_MODULE, INTREPID_CLASS_NAME);
|
||||||
|
#else
|
||||||
|
intrepid_dev_class = class_create(INTREPID_CLASS_NAME);
|
||||||
|
#endif
|
||||||
if (IS_ERR(intrepid_dev_class)) {
|
if (IS_ERR(intrepid_dev_class)) {
|
||||||
ret = PTR_ERR(intrepid_dev_class);
|
ret = PTR_ERR(intrepid_dev_class);
|
||||||
pr_alert("intrepid: failed to create device class, got %d\n", ret);
|
pr_alert("intrepid: failed to create device class, got %d\n", ret);
|
||||||
|
|
@ -835,7 +1330,7 @@ exit:
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
free_net_devices:
|
free_net_devices:
|
||||||
kzfree(net_devices);
|
kfree(net_devices);
|
||||||
free_shared_mem:
|
free_shared_mem:
|
||||||
vfree(shared_mem);
|
vfree(shared_mem);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -852,8 +1347,12 @@ static __exit void intrepid_exit(void)
|
||||||
unregister_chrdev(major_number, INTREPID_DEVICE_NAME);
|
unregister_chrdev(major_number, INTREPID_DEVICE_NAME);
|
||||||
|
|
||||||
for (i = 0; i < MAX_NET_DEVICES; i++) {
|
for (i = 0; i < MAX_NET_DEVICES; i++) {
|
||||||
if (net_devices[i] != NULL)
|
if (net_devices[i] != NULL) {
|
||||||
|
if (net_devices[i]->type == ARPHRD_CAN)
|
||||||
intrepid_remove_can_if(i);
|
intrepid_remove_can_if(i);
|
||||||
|
else if (net_devices[i]->type == ARPHRD_ETHER)
|
||||||
|
intrepid_remove_eth_if(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(net_devices);
|
kfree(net_devices);
|
||||||
|
|
|
||||||
73
neomessage.h
73
neomessage.h
|
|
@ -16,12 +16,12 @@ typedef union {
|
||||||
uint32_t extendedFrame : 1;
|
uint32_t extendedFrame : 1;
|
||||||
uint32_t remoteFrame : 1;
|
uint32_t remoteFrame : 1;
|
||||||
uint32_t crcError : 1;
|
uint32_t crcError : 1;
|
||||||
uint32_t canErrorPassive : 1;
|
uint32_t canErrorPassive : 1; // Occupies the same space as headerCRCError
|
||||||
uint32_t incompleteFrame : 1;
|
uint32_t incompleteFrame : 1;
|
||||||
uint32_t lostArbitration : 1;
|
uint32_t lostArbitration : 1;
|
||||||
uint32_t undefinedError : 1;
|
uint32_t undefinedError : 1;
|
||||||
uint32_t canBusOff : 1;
|
uint32_t canBusOff : 1;
|
||||||
uint32_t canErrorWarning : 1;
|
uint32_t canBusRecovered : 1;
|
||||||
uint32_t canBusShortedPlus : 1;
|
uint32_t canBusShortedPlus : 1;
|
||||||
uint32_t canBusShortedGround : 1;
|
uint32_t canBusShortedGround : 1;
|
||||||
uint32_t checksumError : 1;
|
uint32_t checksumError : 1;
|
||||||
|
|
@ -98,6 +98,10 @@ typedef union {
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef uint16_t neonetid_t;
|
||||||
|
typedef uint8_t neonettype_t;
|
||||||
|
typedef uint16_t neomessagetype_t;
|
||||||
|
|
||||||
#define ICSNEO_NETWORK_TYPE_INVALID ((uint8_t)0)
|
#define ICSNEO_NETWORK_TYPE_INVALID ((uint8_t)0)
|
||||||
#define ICSNEO_NETWORK_TYPE_INTERNAL ((uint8_t)1) // Used for statuses that don't actually need to be transferred to the client application
|
#define ICSNEO_NETWORK_TYPE_INTERNAL ((uint8_t)1) // Used for statuses that don't actually need to be transferred to the client application
|
||||||
#define ICSNEO_NETWORK_TYPE_CAN ((uint8_t)2)
|
#define ICSNEO_NETWORK_TYPE_CAN ((uint8_t)2)
|
||||||
|
|
@ -109,42 +113,73 @@ typedef union {
|
||||||
#define ICSNEO_NETWORK_TYPE_OTHER ((uint8_t)0xFF)
|
#define ICSNEO_NETWORK_TYPE_OTHER ((uint8_t)0xFF)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
neomessage_statusbitfield_t status;
|
uint8_t _reserved1[16];
|
||||||
uint64_t timestamp;
|
uint64_t timestamp;
|
||||||
uint64_t timestampReserved;
|
uint64_t _reservedTimestamp;
|
||||||
const uint8_t* data;
|
uint8_t _reserved2[sizeof(size_t) * 2 + 7 + sizeof(neonetid_t) + sizeof(neonettype_t)];
|
||||||
size_t length;
|
neomessagetype_t messageType;
|
||||||
uint8_t header[4];
|
uint8_t _reserved3[12];
|
||||||
uint16_t netid;
|
|
||||||
uint8_t type;
|
|
||||||
uint8_t reserved[17];
|
|
||||||
} neomessage_t; // 72 bytes total
|
} neomessage_t; // 72 bytes total
|
||||||
// Any time you add another neomessage_*_t type, make sure to add it to the static_asserts below!
|
// Any time you add another neomessage_*_t type, make sure to add it to the static_asserts below!
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
neomessage_statusbitfield_t status;
|
neomessage_statusbitfield_t status;
|
||||||
uint64_t timestamp;
|
uint64_t timestamp;
|
||||||
uint64_t timestampReserved;
|
uint64_t _reservedTimestamp;
|
||||||
|
const uint8_t* data;
|
||||||
|
size_t length;
|
||||||
|
uint8_t header[4];
|
||||||
|
neonetid_t netid;
|
||||||
|
neonettype_t type;
|
||||||
|
uint8_t _reserved0;
|
||||||
|
uint16_t description;
|
||||||
|
neomessagetype_t messageType;
|
||||||
|
uint8_t _reserved1[12];
|
||||||
|
} neomessage_frame_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
neomessage_statusbitfield_t status;
|
||||||
|
uint64_t timestamp;
|
||||||
|
uint64_t _reservedTimestamp;
|
||||||
const uint8_t* data;
|
const uint8_t* data;
|
||||||
size_t length;
|
size_t length;
|
||||||
uint32_t arbid;
|
uint32_t arbid;
|
||||||
uint16_t netid;
|
neonetid_t netid;
|
||||||
uint8_t type;
|
neonettype_t type;
|
||||||
uint8_t dlcOnWire;
|
uint8_t dlcOnWire;
|
||||||
uint8_t reserved[16];
|
uint16_t description;
|
||||||
|
neomessagetype_t messageType;
|
||||||
|
uint8_t _reserved1[12];
|
||||||
} neomessage_can_t;
|
} neomessage_can_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
neomessage_statusbitfield_t status;
|
neomessage_statusbitfield_t status;
|
||||||
uint64_t timestamp;
|
uint64_t timestamp;
|
||||||
uint64_t timestampReserved;
|
uint64_t _reservedTimestamp;
|
||||||
|
size_t _reserved2[2];
|
||||||
|
uint8_t transmitErrorCount;
|
||||||
|
uint8_t receiveErrorCount;
|
||||||
|
uint8_t _reserved3[5];
|
||||||
|
neonetid_t netid;
|
||||||
|
neonettype_t type;
|
||||||
|
neomessagetype_t messageType;
|
||||||
|
uint8_t _reserved4[12];
|
||||||
|
} neomessage_can_error_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
neomessage_statusbitfield_t status;
|
||||||
|
uint64_t timestamp;
|
||||||
|
uint64_t _reservedTimestamp;
|
||||||
const uint8_t* data;
|
const uint8_t* data;
|
||||||
size_t length;
|
size_t length;
|
||||||
uint8_t preemptionFlags;
|
uint8_t preemptionFlags;
|
||||||
uint8_t reservedHeader[3];
|
uint8_t _reservedHeader[3];
|
||||||
uint16_t netid;
|
neonetid_t netid;
|
||||||
uint8_t type;
|
neonettype_t type;
|
||||||
uint8_t reserved[17];
|
uint8_t _reserved0;
|
||||||
|
uint16_t description;
|
||||||
|
neomessagetype_t messageType;
|
||||||
|
uint8_t _reserved1[12];
|
||||||
} neomessage_eth_t;
|
} neomessage_eth_t;
|
||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue