17 Commits
Author SHA1 Message Date
Christian GabrielandKyle Schwarz 7158af4b73 3.1.0
Signed-off-by: Christian Gabriel <ch_gabriel@web.de>
2024-07-23 15:22:00 -04:00
Christian GabrielandKyle Schwarz d66977868f fix: Set CAN state to ERROR_ACTIVE, init supported
Signed-off-by: Christian Gabriel <ch_gabriel@web.de>
2024-07-23 15:22:00 -04:00
Christian GabrielandKyle Schwarz 9dd9a74b9e Add support for bitrate setting
Allow userspace daemon to report currently set bitrates via ioctl.
Also allow setting bitrates via kernel interfaces and report the
new bitrates to userspace daemon

Signed-off-by: Christian Gabriel <ch_gabriel@web.de>
2024-07-23 15:22:00 -04:00
Christian GabrielandKyle Schwarz 760b9d8e34 Add compile-time size checks to neomessage sizes
All versions of the struct need to be the same size. Ensure they are.

Signed-off-by: Christian Gabriel <ch_gabriel@web.de>
2024-07-23 15:22:00 -04:00
Christian GabrielandKyle Schwarz c888a206d9 Save client version
This allows to use old daemons with new versions of the driver
which do not support newer features yet

Signed-off-by: Christian Gabriel <ch_gabriel@web.de>
2024-07-23 15:21:43 -04:00
Christian GabrielandKyle Schwarz b69aaf12ab Add can_priv to intrepid_netdevice
For CAN network devices, the kernel expects struct can_priv as first
member of netdev_priv.

Signed-off-by: Christian Gabriel <ch_gabriel@web.de>
2024-07-23 15:21:43 -04:00
Kyle Schwarz 4841b2863c 3.0.4
- Update copyright
- Add Fedora package requirements to README
2024-01-02 10:24:49 -05:00
Kyle Schwarz 8f5889e129 Add package requirements for Fedora 2024-01-02 10:20:47 -05:00
Kyle Schwarz f92a946a08 Update copyright for 2024 2024-01-02 10:20:23 -05:00
kschwarz-intrepidcsandGitHub 9caa973a5a Merge pull request #16 from genrad/master
Fix for newer 6.4.x Kernel, class_create() sig changed
2023-09-08 10:08:29 -04:00
Kyle Schwarz 67c196ddfe Update daemon version requirement
Bump to 3.1.*
2023-09-07 13:55:08 -04:00
Doug Potts 2ee6101ec2 Fix for newer 6.5 Kernel, class_create() sig changed 2023-09-01 17:00:47 -04:00
Kyle Schwarz f67b4b3620 Bump maximum device count to 64 2023-07-11 22:37:06 -04:00
Kyle Schwarz cd073b2019 3.0.1
Update copyright & changlog
2023-05-05 00:23:51 -04:00
Kyle Schwarz 198cc74e21 Fix usermode daemon version check 2022-09-01 14:29:43 -04:00
Kyle Schwarz e455a8847c Add missing brackets 2022-08-31 16:21:28 -04:00
Gary Chong 5d25e80c35 Added Functionality for Ethernet 2022-08-16 17:21:17 -04:00
3 changed files with 532 additions and 104 deletions
+17
View File
@@ -1,3 +1,20 @@
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
+4 -4
View File
@@ -1,10 +1,10 @@
Version 2.1.0
Version 3.0.4
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.
On Ubuntu, this is accomplished by running `sudo apt install linux-headers-generic build-essential gcc git`
First, install the necessary dependencies for building kernel modules:
- Ubuntu: `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`
+477 -66
View File
@@ -1,7 +1,7 @@
/*
* intrepid.c - Netdevice driver for Intrepid CAN/Ethernet devices
*
* Copyright (c) 2016-2022 Intrepid Control Systems, Inc.
* Copyright (c) 2016-2024 Intrepid Control Systems, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
@@ -48,6 +48,7 @@
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/version.h>
#include <linux/etherdevice.h>
#include "neomessage.h"
@@ -55,9 +56,9 @@
#define str(x) str_first(x)
#define KO_DESC "Netdevice driver for Intrepid CAN/Ethernet devices"
#define KO_MAJOR 2
#define KO_MAJOR 3
#define KO_MINOR 1
#define KO_PATCH 1
#define KO_PATCH 0
#define KO_VERSION str(KO_MAJOR) "." str(KO_MINOR) "." str(KO_PATCH)
#define KO_VERSION_INT (KO_MAJOR << 16) | (KO_MINOR << 8) | KO_PATCH
@@ -73,21 +74,23 @@ MODULE_VERSION(KO_VERSION);
#define INTREPID_DEVICE_NAME "intrepid_netdevice"
#define INTREPID_CLASS_NAME "intrepid"
#define MAX_NET_DEVICES 16
#define MAX_NET_DEVICES 64
#define SHARED_MEM_SIZE 0x400000
#define SIOCSADDIF 0x3001
#define SIOCSREMOVEIF 0x3002
#define SIOCGSHAREDMEMSIZE 0x3003
#define SIOCSMSGSWRITTEN 0x3004
#define SIOCGMAXIFACES 0x3005
#define SIOCGVERSION 0x3006
#define SIOCGCLIENTVEROK 0x3007
#define SIOCSADDCANIF 0x3001
#define SIOCSADDETHIF 0x3002
#define SIOCSREMOVECANIF 0x3003
#define SIOCSREMOVEETHIF 0x3004
#define SIOCGSHAREDMEMSIZE 0x3005
#define SIOCSMSGSWRITTEN 0x3006
#define SIOCGMAXIFACES 0x3007
#define SIOCGVERSION 0x3008
#define SIOCGCLIENTVEROK 0x3009
#define SIOCSBAUDRATE 0x300A
/* This is true until we have Ethernet support
* 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_FAULT_TAKES_VMA (LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0))
@@ -107,16 +110,17 @@ struct intrepid_pending_tx_info {
struct intrepid_netdevice {
struct can_priv can;
struct can_berr_counter bec;
struct net_device *dev;
spinlock_t lock;
int is_stopped;
unsigned char *from_user;
uint8_t tx_idx;
int bitrate_changed;
};
static int is_open;
static int major_number;
static int client_version;
static unsigned char *shared_mem;
static struct class *intrepid_dev_class;
static struct device *intrepid_dev;
@@ -202,7 +206,7 @@ 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;
struct intrepid_netdevice *ics = netdev_priv(dev);
@@ -223,7 +227,7 @@ static netdev_tx_t intrepid_netdevice_xmit(struct sk_buff *skb, struct net_devic
if (unlikely(ics->is_stopped)) {
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;
goto exit;
}
@@ -252,7 +256,6 @@ 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);
goto exit;
}
msg.status.remoteFrame = true;
}
@@ -268,7 +271,6 @@ static netdev_tx_t intrepid_netdevice_xmit(struct sk_buff *skb, struct net_devic
ret = NETDEV_TX_BUSY;
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
@@ -285,9 +287,63 @@ static netdev_tx_t intrepid_netdevice_xmit(struct sk_buff *skb, struct net_devic
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_can_t) + MAX_MTU))
if (intrepid_tx_box_no_space_for(sizeof(neomessage_can_t) + CANFD_MTU))
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);
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 - ETH_HLEN;
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 - ETH_HLEN);
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);
@@ -301,8 +357,6 @@ static int intrepid_netdevice_stop(struct net_device *dev)
{
struct intrepid_netdevice *ics = netdev_priv(dev);
close_candev(dev);
spin_lock_bh(&ics->lock);
netif_stop_queue(dev);
netif_carrier_off(dev);
@@ -323,10 +377,19 @@ static int intrepid_netdevice_open(struct net_device *dev)
// 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_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,
};
@@ -358,6 +421,68 @@ static int intrepid_remove_can_if(int index)
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)
{
// The `requestedName` parameter is always NULL if KERNEL_SUPPORTS_ALIASES is false
@@ -388,15 +513,14 @@ static int intrepid_add_can_if(struct intrepid_netdevice **result, const char *r
goto exit;
}
dev->base_addr = i;
dev->flags |= IFF_ECHO;
#if KERNEL_CHECKS_MTU_RANGE
dev->min_mtu = CAN_MTU;
dev->max_mtu = MAX_MTU;
dev->max_mtu = CANFD_MTU;
#endif
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 (requestedName && ((aliasLen = strlen(requestedName)) > 0) && aliasLen < IFALIASZ) {
dev->ifalias = kzalloc(sizeof(struct dev_ifalias) + aliasLen + 1, GFP_KERNEL);
@@ -414,6 +538,17 @@ static int intrepid_add_can_if(struct intrepid_netdevice **result, const char *r
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);
ret = register_candev(dev);
@@ -520,7 +655,7 @@ static int intrepid_fill_can_frame_from_neomessage(
* If false is returned, this message should be handled as a receive
* message, regardless of the transmit flag.
*/
static bool handle_transmit_receipt(
static bool handle_CAN_transmit_receipt(
struct net_device *device,
const neomessage_can_t *msg,
const uint8_t *data,
@@ -560,6 +695,169 @@ static bool handle_transmit_receipt(
return true;
}
static bool handle_ETH_transmit_receipt(
struct net_device *device,
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_ECHO;
#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 sset to %s\n", dev->name, requestedName);
}
}
#endif
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,
struct net_device *device)
{
struct ethhdr *eth;
if (unlikely(msg->length > (ETH_FRAME_LEN + ETH_FCS_LEN))) {
stats->rx_dropped++;
pr_warn("intrepid: Dropping message on %s, invalid message length %ld", device->name, msg->length);
return 1;
}
if (unlikely(skb == NULL)) {
stats->rx_dropped++;
pr_warn("intrepid: Dropping message on %s, skb allocation failed", device->name);
return 1;
}
skb_reset_mac_header(skb);
eth = skb_put(skb, sizeof(struct ethhdr));
*eth = *(struct ethhdr*)data;
skb->protocol = htons(ETH_P_802_3);
skb_put_data(skb, data + ETH_HLEN, msg->length);
stats->rx_bytes += msg->length;
stats->rx_packets++;
return 0;
}
static struct sk_buff *intrepid_skb_from_neomessage(
struct net_device *device,
const neomessage_frame_t *msg_generic,
@@ -567,8 +865,6 @@ static struct sk_buff *intrepid_skb_from_neomessage(
struct net_device_stats *stats)
{
struct sk_buff *skb = NULL;
struct canfd_frame* cf = NULL;
const neomessage_can_t* msg = NULL;
int ret = 0;
/* input validation */
@@ -577,65 +873,77 @@ static struct sk_buff *intrepid_skb_from_neomessage(
pr_warn("intrepid: Dropping message on %s, skb from neomessage input validation failed", device->name);
goto out;
}
if (unlikely(msg_generic->type != ICSNEO_NETWORK_TYPE_CAN)) {
stats->rx_dropped++;
pr_warn("intrepid: Dropping message on %s, wrong type %d", device->name, (int)msg_generic->type);
switch (msg_generic->type) {
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;
}
msg = (const neomessage_can_t*)msg_generic;
/* if this message is handled as a transmit receipt,
* don't turn it into a receive skb here.
*/
if (handle_transmit_receipt(device, msg, data, stats))
goto out;
if (msg->status.globalError)
if (can_msg->status.globalError)
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);
else
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)) {
stats->rx_dropped++;
pr_warn("intrepid: Dropping message on %s, skb allocation failed", device->name);
goto out;
}
switch(msg->type) {
case ICSNEO_NETWORK_TYPE_CAN:
if (msg->status.globalError)
if (can_msg->status.globalError)
ret = intrepid_fill_canerr_frame_from_neomessage(
stats,
(struct can_frame*)cf,
(const neomessage_can_t*)msg);
else if (msg->status.canfdFDF)
(const neomessage_can_t*)can_msg);
else if (can_msg->status.canfdFDF)
ret = intrepid_fill_canfd_frame_from_neomessage(
stats,
cf,
(const neomessage_can_t*)msg,
(const neomessage_can_t*)can_msg,
data);
else
ret = intrepid_fill_can_frame_from_neomessage(
stats,
(struct can_frame*)cf,
(const neomessage_can_t*)msg,
(const neomessage_can_t*)can_msg,
data);
}
break;
case ICSNEO_NETWORK_TYPE_ETHERNET:
{
const neomessage_eth_t *msg = (const neomessage_eth_t*)msg_generic;
if (handle_ETH_transmit_receipt(device, msg, data, stats))
goto out;
skb = netdev_alloc_skb(device, ETH_DATA_LEN);
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,
device);
}
break;
default:
pr_warn("intrepid: Dropping message on %s, invalid type %d", device->name, msg->type);
pr_warn("intrepid: Dropping message on %s, invalid type %d", device->name, msg_generic->type);
goto out;
}
if (unlikely(ret != 0)) {
pr_warn("intrepid: Dropping message on %s, frame fill failed", device->name);
goto out;
}
out:
return skb;
}
@@ -671,8 +979,8 @@ static int intrepid_read_messages(int device_index, unsigned int count)
data = currentPosition;
currentPosition += msg->length;
/* pass along the converted message to the kernel for dispatch */
skb = intrepid_skb_from_neomessage(device, msg, data, stats);
/* pass along the converted message to the kernel for dispatch */
if (skb != NULL)
ret = netif_rx(skb);
@@ -693,7 +1001,7 @@ static long intrepid_dev_ioctl(struct file *fp, unsigned int cmd, unsigned long
}
switch(cmd) {
case SIOCSADDIF: {
case SIOCSADDCANIF: {
struct intrepid_netdevice *result = NULL;
#if KERNEL_SUPPORTS_ALIASES
char requestedNameBuffer[IFALIASZ] = {0};
@@ -711,9 +1019,46 @@ static long intrepid_dev_ioctl(struct file *fp, unsigned int cmd, unsigned long
#endif
break;
}
case SIOCSREMOVEIF:
case SIOCSREMOVECANIF:
ret = intrepid_remove_can_if (arg);
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:
ret = SHARED_MEM_SIZE;
break;
@@ -724,13 +1069,8 @@ static long intrepid_dev_ioctl(struct file *fp, unsigned int cmd, unsigned long
ret = KO_VERSION_INT;
break;
case SIOCGCLIENTVEROK:
/* Here we can do checks to see if the usermode daemon is
* a compatible version with us. We don't enforce anything
* 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))
client_version = arg;
if (VER_MAJ_FROM_INT(arg) == 3 && VER_MIN_FROM_INT(arg) >= 1 && VER_PATCH_FROM_INT(arg) >= 0)
ret = 0; /* ok to start */
else
ret = 1;
@@ -795,14 +1135,45 @@ static int intrepid_dev_release(struct inode *ip, struct file *fp)
wake_up_interruptible(&tx_wait);
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);
else if (net_devices[i]->type == ARPHRD_ETHER)
intrepid_remove_eth_if(i);
}
}
is_open = 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
* 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 */
@@ -814,6 +1185,15 @@ static ssize_t intrepid_dev_read(struct file *fp, char *buffer, size_t len, loff
if (len < sizeof(info))
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);
/* fill out the info for the user */
@@ -848,6 +1228,18 @@ static unsigned int intrepid_dev_poll(struct file *fp, poll_table *wait)
if (tx_box_count[current_tx_box] > 0)
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;
}
@@ -865,6 +1257,17 @@ static __init int intrepid_init(void)
int ret;
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;
/* this is the shared memory used to transfer between us and the user daemon */
@@ -906,7 +1309,11 @@ static __init int intrepid_init(void)
return -1;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
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)) {
ret = PTR_ERR(intrepid_dev_class);
pr_alert("intrepid: failed to create device class, got %d\n", ret);
@@ -947,8 +1354,12 @@ static __exit void intrepid_exit(void)
unregister_chrdev(major_number, INTREPID_DEVICE_NAME);
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);
else if (net_devices[i]->type == ARPHRD_ETHER)
intrepid_remove_eth_if(i);
}
}
kfree(net_devices);