Initial commit of Version 2.0.0, with CAN FD support

pull/4/head
Paul Hollinsky 2019-05-02 19:24:20 -04:00
parent d8ea05895e
commit 3bbef8679d
7 changed files with 774 additions and 516 deletions

6
.gitignore vendored
View File

@ -32,6 +32,7 @@ modules.builtin
*.lzo
*.patch
*.gcno
*.old
#
# Top-level generic files
@ -56,6 +57,11 @@ Module.symvers
!.gitignore
!.mailmap
#
# Visual Studio Code
#
!.vscode
#
# Generated include files
#

5
.vscode/settings.json vendored 100644
View File

@ -0,0 +1,5 @@
{
"editor.tabSize": 8,
"editor.insertSpaces": false,
"editor.rulers": [80]
}

View File

@ -8,3 +8,9 @@ install:
depmod -a
clean:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
reload: all
-sudo rmmod intrepid
sudo modprobe can
sudo modprobe can_raw
sudo modprobe can_dev
sudo insmod intrepid.ko

View File

@ -1,13 +1,23 @@
This is the kernel object portion of 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.
Version 2.0.0
First, build the KO.
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.
```
$ make
```
First, install the necessary dependencies for building kernel modules.
On Ubuntu, this is acomplished by running `sudo apt install linux-headers-generic build-essential gcc git`
Clone this repository by running `git clone https://github.com/intrepidcs/intrepid-socketcan-kernel-module.git`
Change into the resulting clone, `cd intrepid-socketcan-kernel-module`
Then, build the KO, `make`
The resulting file will be ```intrepid.ko```. This module depends on ```can```, ```can_dev```, and ```can_raw``` (which should already be a part of your system). Before you can load the module, make sure these modules are loaded, then ```insmod``` can be used to load it.
A script is provided to help within the makefile, `make reload`.
If you prefer to run it yourself, you can run
```
$ sudo modprobe can
$ sudo modprobe can_raw
@ -15,7 +25,9 @@ $ sudo modprobe can_dev
$ sudo insmod intrepid.ko
```
```lsmod``` can confirm the module is loaded. If you wish you have the module auto-load on boot run ```make install``` once the module is built.
```lsmod``` can confirm the module is loaded. At this point, you can refer to the [icsscand](https://github.com/intrepidcs/icsscand) instructions.
If you wish you have the module auto-load on boot run ```make install``` once the module is built.
```
$ sudo make install

View File

@ -1,7 +1,7 @@
/*
* intrepid.c - Netdevice driver for Intrepid CAN/Ethernet devices
*
* Copyright (c) 2016 Intrepid Control Systems, Inc.
* Copyright (c) 2016-2019 Intrepid Control Systems, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
@ -49,71 +49,50 @@
#include <linux/poll.h>
#include <linux/version.h>
#include "neomessage.h"
#define str_first(x) #x
#define str(x) str_first(x)
#define KO_DESC "Netdevice driver for Intrepid CAN/Ethernet devices"
#define KO_VERSION "1.0"
#define KO_MAJOR 2
#define KO_MINOR 0
#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
#define VER_MAJ_FROM_INT(VERINT) ((VERINT >> 16) & 0xFF)
#define VER_MIN_FROM_INT(VERINT) ((VERINT >> 8) & 0xFF)
#define VER_PATCH_FROM_INT(VERINT) (VERINT & 0xFF)
MODULE_DESCRIPTION(KO_DESC);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Paul Hollinsky <phollinsky@intrepidcs.com>");
MODULE_AUTHOR("Jeffrey Quesnelle <jeffq@intrepidcs.com>");
MODULE_VERSION(KO_VERSION);
#define INTREPID_DEVICE_NAME "intrepid_netdevice"
#define INTREPID_CLASS_NAME "intrepid"
#define MAX_NET_DEVICES 16
#define SHARED_MEM_SIZE 0x100000
#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 SPY_STATUS_GLOBAL_ERR 0x01
#define SPY_STATUS_TX_MSG 0x02
#define SPY_STATUS_XTD_FRAME 0x04
#define SPY_STATUS_REMOTE_FRAME 0x08
#define SPY_STATUS_LOST_ARBITRATION 0x80
#define SPY_STATUS_BUS_SHORTED_PLUS 0x800
#define SPY_STATUS_VSI_TX_UNDERRUN 0x8000000
struct icsSpyMessage {
unsigned int StatusBitField;
unsigned int StatusBitField2;
unsigned int TimeHardware;
unsigned int TimeHardware2;
unsigned int TimeSystem;
unsigned int TimeSystem2;
unsigned char TimeStampHardwareID;
unsigned char TimeStampSystemID;
unsigned char NetworkID;
unsigned char NodeID;
unsigned char Protocol;
unsigned char MessagePieceID;
unsigned char ExtraDataPtrEnabled;
unsigned char NumberBytesHeader;
unsigned char NumberBytesData;
unsigned char NetworkID2;
unsigned short DescriptionID;
unsigned int ArbIDOrHeader;
unsigned char Data[8];
union
{
struct
{
unsigned int StatusBitField3;
unsigned int StatusBitField4;
};
unsigned char AckBytes[8];
};
void* ExtraDataPtr;
unsigned char MiscData;
unsigned char Reserved[3];
} icsSpyMessage;
/* 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
struct intrepid_pending_tx_info {
int tx_box_index;
int count;
size_t bytes;
};
struct intrepid_netdevice {
@ -127,84 +106,156 @@ struct intrepid_netdevice {
static int is_open;
static int major_number;
static int current_tx_box;
static int tx_box_count[2];
static unsigned char *shared_mem;
static unsigned char *tx_boxes[2];
static struct class *intrepid_dev_class;
static struct device *intrepid_dev;
static struct net_device **net_devices;
static struct mutex ioctl_mutex;
static spinlock_t tx_box_lock;
static wait_queue_head_t tx_wait;
static unsigned char *tx_boxes[2];
static int current_tx_box;
static int tx_box_count[2];
static size_t tx_box_bytes[2];
static spinlock_t tx_box_lock;
#define RX_BOX_SIZE (SHARED_MEM_SIZE / (MAX_NET_DEVICES * 2))
#define TX_BOX_SIZE (SHARED_MEM_SIZE / 4)
#define GET_RX_BOX(DEVICE_INDEX) (shared_mem + (RX_BOX_SIZE * DEVICE_INDEX))
#define GET_TX_BOX(INDEX) (shared_mem + (SHARED_MEM_SIZE / 2) + (INDEX * TX_BOX_SIZE))
#define MAX_NUM_RX_MSGS (RX_BOX_SIZE / sizeof(struct icsSpyMessage))
#define MAX_NUM_TX_MSGS (TX_BOX_SIZE / sizeof(struct icsSpyMessage))
#define GET_RX_BOX(DEVICE_INDEX) \
(shared_mem + (RX_BOX_SIZE * DEVICE_INDEX))
#define GET_TX_BOX(BOX_INDEX) \
(shared_mem + (SHARED_MEM_SIZE / 2) + (BOX_INDEX * TX_BOX_SIZE))
static netdev_tx_t intrepid_netdevice_xmit(struct sk_buff *skb, struct net_device *dev)
/* 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)
{
struct net_device_stats *stats = &dev->stats;
struct intrepid_netdevice *ics = netdev_priv(dev);
struct can_frame *cf = (struct can_frame *)skb->data;
struct icsSpyMessage msg = {0};
struct icsSpyMessage *box;
return (tx_box_bytes[current_tx_box] + size >= TX_BOX_SIZE - 1);
}
stats->tx_packets++;
stats->tx_bytes = get_can_dlc(cf->can_dlc);
static void intrepid_unpause_all_queues(void)
{
int i;
for(i = 0; i < MAX_NET_DEVICES; ++i) {
struct net_device *dev = net_devices[i];
struct intrepid_netdevice *ics;
if (dev == NULL)
continue;
if (can_dropped_invalid_skb(dev, skb))
{
pr_info("intrepid: dropping invalid frame on %s\n", dev->name);
return NETDEV_TX_OK;
ics = netdev_priv(dev);
if (ics->is_stopped) {
spin_lock_bh(&ics->lock);
netif_wake_queue(dev);
ics->is_stopped = 0;
spin_unlock_bh(&ics->lock);
}
/* convert the can_frame to an icsSpyMessage */
if (cf->can_id & CAN_EFF_FLAG)
{
msg.ArbIDOrHeader = cf->can_id & CAN_EFF_MASK;
msg.StatusBitField = SPY_STATUS_XTD_FRAME;
}
else
msg.ArbIDOrHeader = cf->can_id & CAN_SFF_MASK;
}
if (cf->can_id & CAN_RTR_FLAG)
msg.StatusBitField |= SPY_STATUS_XTD_FRAME;
else
{
msg.NumberBytesData = get_can_dlc(cf->can_dlc); /* is can_dlc sanitizied already? */
memcpy(msg.Data, cf->data, msg.NumberBytesData);
}
static void intrepid_pause_all_queues(void)
{
int i;
for(i = 0; i < MAX_NET_DEVICES; ++i) {
struct net_device *dev = net_devices[i];
struct intrepid_netdevice *ics;
if (dev == NULL)
continue;
msg.NumberBytesHeader = 2;
msg.NetworkID = dev->base_addr;
/* copy the message over into the usermode box. if this message fills up the box,
* turn of the queue until the user reads it out */
spin_lock_bh(&tx_box_lock);
box = (struct icsSpyMessage*)tx_boxes[current_tx_box];
memcpy(box + tx_box_count[current_tx_box], &msg, sizeof(struct icsSpyMessage));
if (++tx_box_count[current_tx_box] == MAX_NUM_TX_MSGS)
{
ics = netdev_priv(dev);
if (!ics->is_stopped) {
spin_lock_bh(&ics->lock);
ics->is_stopped = 1;
netif_stop_queue(dev);
spin_unlock_bh(&ics->lock);
}
}
}
spin_unlock_bh(&tx_box_lock);
static netdev_tx_t intrepid_netdevice_xmit(struct sk_buff *skb, struct net_device *dev)
{
int ret = NETDEV_TX_OK;
struct net_device_stats *stats = &dev->stats;
struct intrepid_netdevice *ics = netdev_priv(dev);
struct canfd_frame *cf = (struct canfd_frame*)skb->data;
bool fd = can_is_canfd_skb(skb);
bool needs_unlock = false;
neomessage_can_t msg = {0};
wake_up_interruptible(&tx_wait);
stats->tx_packets++;
stats->tx_bytes = cf->len;
if (can_dropped_invalid_skb(dev, skb)) {
pr_info("intrepid: dropping invalid frame on %s\n", dev->name);
goto exit;
}
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_can_t) + MAX_MTU)) {
ret = NETDEV_TX_BUSY;
goto exit;
}
pr_warn("intrepid: device should not have been stopped, waking all\n");
intrepid_unpause_all_queues();
}
/* convert the canfd_frame to a neomessage_can_t */
if (cf->can_id & CAN_EFF_FLAG) {
msg.arbid = cf->can_id & CAN_EFF_MASK;
msg.status.extendedFrame = true;
} else {
msg.arbid = cf->can_id & CAN_SFF_MASK;
}
if (fd) {
msg.status.canfdFDF = true;
if (cf->flags & CANFD_BRS)
msg.status.canfdBRS = true;
if (cf->flags & CANFD_ESI)
msg.status.canfdESI = true;
}
if (cf->can_id & CAN_RTR_FLAG) {
if (unlikely(fd)) {
pr_info("intrepid: tried to send RTR frame on CANFD %s\n", dev->name);
goto exit;
}
msg.status.remoteFrame = true;
}
msg.length = cf->len;
msg.netid = dev->base_addr;
if (intrepid_tx_box_no_space_for(sizeof(neomessage_can_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_can_t) + msg.length);
pr_err("intrepid: %ld length message caused NETDEV_TX_BUSY (%ld)\n", msg.length, offset);
ret = NETDEV_TX_BUSY;
goto exit;
}
/* Copy the message into the usermode box */
memcpy(tx_boxes[current_tx_box] + tx_box_bytes[current_tx_box], &msg, sizeof(neomessage_can_t));
tx_box_bytes[current_tx_box] += sizeof(neomessage_can_t);
memcpy(tx_boxes[current_tx_box] + tx_box_bytes[current_tx_box], cf->data, msg.length);
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_can_t) + MAX_MTU))
intrepid_pause_all_queues();
exit:
if(ret == NETDEV_TX_OK)
consume_skb(skb);
return NETDEV_TX_OK;
wake_up_interruptible(&tx_wait);
if(needs_unlock)
spin_unlock_bh(&tx_box_lock);
return ret;
}
static int intrepid_netdevice_stop(struct net_device *dev)
@ -221,79 +272,72 @@ static int intrepid_netdevice_stop(struct net_device *dev)
static int intrepid_netdevice_open(struct net_device *dev)
{
netif_start_queue(dev);
return 0;
}
static int intrepid_netdevice_change_mtu(struct net_device *dev, int new_mtu)
{
return -EINVAL;
}
// static int intrepid_netdevice_change_mtu(struct net_device *dev, int new_mtu)
// {
// return -EINVAL;
// }
static const struct net_device_ops intrepid_netdevice_ops = {
.ndo_open = intrepid_netdevice_open,
.ndo_stop = intrepid_netdevice_stop,
.ndo_start_xmit = intrepid_netdevice_xmit,
.ndo_change_mtu = intrepid_netdevice_change_mtu,
//.ndo_change_mtu = intrepid_netdevice_change_mtu,
};
static void intrepid_netdevice_free(struct net_device *dev)
{
int i;
i = dev->base_addr;
free_candev(dev);
net_devices[i] = NULL;
}
static struct net_device* intrepid_get_dev_by_index(int index)
{
if (index < 0 || index >= MAX_NET_DEVICES)
return NULL;
if (net_devices[index] == NULL)
return NULL;
return net_devices[index];
}
static int intrepid_remove_if(int index)
static int intrepid_remove_can_if(int index)
{
struct net_device *device = intrepid_get_dev_by_index(index);
if (!device)
return -EINVAL;
pr_info("intrepid: Removing device %s\n", device->name);
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_candev(device);
net_devices[index] = NULL;
pr_info("intrepid: Removed device %d\n", index);
return 0;
}
static int intrepid_add_if(struct intrepid_netdevice** result)
static int intrepid_add_can_if(struct intrepid_netdevice **result, const char *requestedName)
{
int i, ret = -EPERM;
size_t aliasLen = 0;
int i;
int ret = -EPERM;
struct net_device *dev = NULL;
struct intrepid_netdevice *ics = NULL;
*result = NULL;
for (i = 0 ; i < MAX_NET_DEVICES ; ++i)
{
for (i = 0; i < MAX_NET_DEVICES; i++) {
if (net_devices[i] == NULL)
break;
}
if (i >= MAX_NET_DEVICES)
{
if (i >= MAX_NET_DEVICES) {
pr_alert("intrepid: No more netdevices available\n");
ret = -ENFILE;
goto exit;
}
dev = alloc_candev(sizeof(*ics), 0);
if (!dev)
{
dev = alloc_candev(sizeof(*ics), 1);
if (!dev) {
pr_alert("intrepid: Could not allocate candev\n");
goto exit;
}
@ -301,13 +345,18 @@ static int intrepid_add_if(struct intrepid_netdevice** result)
dev->base_addr = i;
dev->flags |= IFF_ECHO;
dev->min_mtu = CAN_MTU;
dev->max_mtu = CANFD_MTU;
dev->netdev_ops = &intrepid_netdevice_ops;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,11,9)
dev->destructor = intrepid_netdevice_free;
#else
dev->needs_free_netdev = true;
dev->priv_destructor = intrepid_netdevice_free;
#endif
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("intrepid: Could not allocate space for ifalias %lu\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);
}
}
ics = netdev_priv(dev);
ics->dev = dev;
ics->is_stopped = 0;
@ -316,28 +365,174 @@ static int intrepid_add_if(struct intrepid_netdevice** result)
spin_lock_init(&ics->lock);
ret = register_candev(dev);
if (ret)
{
if (ret) {
pr_alert("intrepid: Could not register candev\n");
free_candev(dev);
goto exit;
}
if (dev_set_mtu(dev, CANFD_MTU)) {
pr_alert("intrepid: Could not set MTU\n");
}
net_devices[i] = dev;
*result = ics;
ret = i;
pr_info("intrepid: Allocated new netdevice %s\n", dev->name);
pr_info("intrepid: Allocated new netdevice %s @ %d\n", dev->name, ret);
exit:
return ret;
}
static int intrepid_fill_canerr_frame_from_neomessage(
struct net_device_stats *stats,
struct can_frame *cf,
const neomessage_can_t *msg)
{
if (msg->status.transmitMessage) {
stats->tx_errors++;
if (msg->status.lostArbitration) {
cf->can_id |= CAN_ERR_LOSTARB;
cf->data[0] = CAN_ERR_LOSTARB_UNSPEC;
} else if (msg->status.vsiTXUnderrun) {
cf->can_id |= CAN_ERR_ACK;
} else {
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
cf->data[2] = CAN_ERR_PROT_TX;
}
} else {
stats->rx_over_errors++;
stats->rx_errors++;
if (msg->status.canBusShortedPlus) {
cf->can_id |= CAN_ERR_TRX | CAN_ERR_BUSERROR;
cf->data[4] = CAN_ERR_TRX_CANH_SHORT_TO_BAT | CAN_ERR_TRX_CANL_SHORT_TO_BAT;
} else {
cf->can_id |= CAN_ERR_BUSERROR;
}
}
return 0;
}
static int intrepid_fill_canfd_frame_from_neomessage(
struct net_device_stats *stats,
struct canfd_frame *cf,
const neomessage_can_t *msg,
const uint8_t *data)
{
if (msg->status.extendedFrame)
cf->can_id |= (msg->arbid & CAN_EFF_MASK) | CAN_EFF_FLAG;
else
cf->can_id |= msg->arbid & CAN_SFF_MASK;
if (msg->status.canfdBRS)
cf->flags |= CANFD_BRS;
if (msg->status.canfdESI)
cf->flags |= CANFD_ESI;
cf->len = msg->length;
memcpy(cf->data, data, cf->len);
stats->rx_bytes += cf->len;
stats->rx_packets++;
return 0;
}
static int intrepid_fill_can_frame_from_neomessage(
struct net_device_stats *stats,
struct can_frame *cf,
const neomessage_can_t *msg,
const uint8_t *data)
{
if (msg->status.extendedFrame)
cf->can_id |= (msg->arbid & CAN_EFF_MASK) | CAN_EFF_FLAG;
else
cf->can_id |= msg->arbid & CAN_SFF_MASK;
if (msg->status.remoteFrame)
cf->can_id |= CAN_RTR_FLAG;
cf->can_dlc = get_can_dlc(msg->length);
memcpy(cf->data, data, cf->can_dlc);
stats->rx_bytes += cf->can_dlc;
stats->rx_packets++;
return 0;
}
static struct sk_buff *intrepid_skb_from_neomessage(
struct net_device *device,
const neomessage_t *msg,
const uint8_t *data,
struct net_device_stats *stats)
{
struct sk_buff *skb = NULL;
struct canfd_frame* cf = NULL;
int ret = 0;
/* input validation */
if (unlikely(device == NULL || msg == NULL || data == NULL || stats == NULL)) {
stats->rx_dropped++;
pr_warn("intrepid: Dropping message on %s, skb from neomessage input validation failed", device->name);
goto fail;
}
if (msg->status.globalError)
skb = alloc_can_err_skb(device, (struct can_frame**)&cf);
else if (msg->status.canfdFDF)
skb = alloc_canfd_skb(device, &cf);
else
skb = alloc_can_skb(device, (struct can_frame**)&cf);
if (unlikely(skb == NULL)) {
stats->rx_dropped++;
pr_warn("intrepid: Dropping message on %s, skb allocation failed", device->name);
goto fail;
}
switch(msg->type) {
case ICSNEO_NETWORK_TYPE_CAN:
if (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)
ret = intrepid_fill_canfd_frame_from_neomessage(
stats,
cf,
(const neomessage_can_t*)msg,
data);
else
ret = intrepid_fill_can_frame_from_neomessage(
stats,
(struct can_frame*)cf,
(const neomessage_can_t*)msg,
data);
break;
default:
pr_warn("intrepid: Dropping message on %s, invalid type %d", device->name, msg->type);
goto fail;
}
if (unlikely(ret != 0)) {
pr_warn("intrepid: Dropping message on %s, frame fill failed", device->name);
goto fail;
}
fail:
return skb;
}
static int intrepid_read_messages(int device_index, unsigned int count)
{
unsigned int i;
struct icsSpyMessage* msg;
const uint8_t* currentPosition;
struct intrepid_netdevice* ics;
struct net_device_stats *stats;
struct net_device *device = intrepid_get_dev_by_index(device_index);
@ -346,99 +541,32 @@ static int intrepid_read_messages(int device_index, unsigned int count)
stats = &device->stats;
ics = netdev_priv(device);
msg = (struct icsSpyMessage*)ics->from_user;
currentPosition = ics->from_user;
if (count != 1)
pr_info("intrepid: reading %d messages\n", count);
if (count > MAX_NUM_RX_MSGS)
count = MAX_NUM_RX_MSGS;
/* ics->from_user is where usermode copied in some icsSpyMessages that need
/* ics->from_user is where usermode copied in some neomessage_ts that need
* to be pumped into the receive plumbing of the interface. loop over them,
* converting icsSpyMessage to a CAN sk_buff */
* converting neomessage_t to a CAN sk_buff */
for (i = 0 ; i < count ; ++i, ++msg)
{
int ret;
int is_error;
struct can_frame *cf;
while (count--) {
const neomessage_t *msg;
const uint8_t *data;
struct sk_buff *skb;
int ret = 0;
is_error = msg->StatusBitField & SPY_STATUS_GLOBAL_ERR;
if (is_error)
skb = alloc_can_err_skb(device, &cf);
else
skb = alloc_can_skb(device, &cf);
if (unlikely(skb == NULL))
{
stats->rx_dropped++;
WARN_ONCE(1, "intrepid: Dropped message on %s: no memory\n",
device->name);
continue;
}
/* if this is a regular message copy over the data bytes and ID, otherwise for
* an error fill out what type of error it is */
if (is_error)
{
if (msg->StatusBitField & SPY_STATUS_TX_MSG)
{
if (msg->StatusBitField & SPY_STATUS_LOST_ARBITRATION)
{
cf->can_id |= CAN_ERR_LOSTARB;
cf->data[0] = CAN_ERR_LOSTARB_UNSPEC;
}
else if (msg->StatusBitField & SPY_STATUS_VSI_TX_UNDERRUN)
{
cf->can_id |= CAN_ERR_ACK;
}
else
{
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
cf->data[2] = CAN_ERR_PROT_TX;
}
stats->tx_errors++;
}
else
{
if (msg->StatusBitField & SPY_STATUS_BUS_SHORTED_PLUS)
{
cf->can_id |= CAN_ERR_TRX | CAN_ERR_BUSERROR;
cf->data[4] = CAN_ERR_TRX_CANH_SHORT_TO_BAT |
CAN_ERR_TRX_CANL_SHORT_TO_BAT;
}
else
{
cf->can_id |= CAN_ERR_BUSERROR;
}
stats->rx_over_errors++;
stats->rx_errors++;
}
}
else
{
if (msg->StatusBitField & SPY_STATUS_XTD_FRAME)
cf->can_id |= (msg->ArbIDOrHeader & CAN_EFF_MASK)
| CAN_EFF_FLAG;
else
cf->can_id |= msg->ArbIDOrHeader & CAN_SFF_MASK;
if (msg->StatusBitField & SPY_STATUS_REMOTE_FRAME)
cf->can_id |= CAN_RTR_FLAG;
cf->can_dlc = get_can_dlc(msg->NumberBytesData);
memcpy(cf->data, msg->Data, cf->can_dlc);
}
msg = (const neomessage_t*)currentPosition;
currentPosition += sizeof(neomessage_t);
data = currentPosition;
currentPosition += msg->length;
/* pass along the converted message to the kernel for dispatch */
skb = intrepid_skb_from_neomessage(device, msg, data, stats);
if (likely(skb != NULL))
ret = netif_rx(skb);
WARN_ONCE(ret == NET_RX_DROP,
"intrepid: Dropping message on %s: backlog full\n", device->name);
/* update our interface's stats */
stats->rx_bytes += cf->can_dlc;
stats->rx_packets++;
if (ret == NET_RX_DROP)
pr_warn("intrepid: Dropping message on %s, dropped by kernel", device->name);
}
return 0;
@ -452,26 +580,21 @@ static long intrepid_dev_ioctl(struct file *fp, unsigned int cmd, unsigned long
return -ERESTARTSYS;
}
switch (cmd)
{
case SIOCSADDIF:
{
switch (cmd) {
case SIOCSADDIF: {
struct intrepid_netdevice *result = NULL;
char requestedNameBuffer[IFALIASZ] = {0};
char* requestedName = NULL;
if ((void __user*)arg != NULL) {
copy_from_user(requestedNameBuffer, (void __user*)arg, IFALIASZ);
requestedName = requestedNameBuffer;
}
ret = intrepid_add_if(&result);
if (result && (ret >= 0) && arg)
{
int len = strlen(result->dev->name) + 1;
if (copy_to_user((void __user *)arg, result->dev->name, len))
{
intrepid_remove_if(ret);
ret = -EFAULT;
ret = intrepid_add_can_if(&result, requestedName);
break;
}
}
} break;
case SIOCSREMOVEIF:
ret = intrepid_remove_if(arg);
ret = intrepid_remove_can_if (arg);
break;
case SIOCGSHAREDMEMSIZE:
ret = SHARED_MEM_SIZE;
@ -479,14 +602,26 @@ static long intrepid_dev_ioctl(struct file *fp, unsigned int cmd, unsigned long
case SIOCGMAXIFACES:
ret = MAX_NET_DEVICES;
break;
case SIOCSMSGSWRITTEN:
{
case SIOCGVERSION:
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. For now, being version 2.X.X is good.
*/
if (VER_MAJ_FROM_INT(arg) == 2)
ret = 0; /* ok to start */
else
ret = 1;
break;
case SIOCSMSGSWRITTEN: {
int index = (int)(arg >> 16);
unsigned int count = (int)(arg & 0xffff);
ret = intrepid_read_messages(index, count);
}
break;
}
} /* end switch (cmd) */
mutex_unlock(&ioctl_mutex);
@ -525,19 +660,11 @@ static int intrepid_dev_open(struct inode *ip, struct file *fp)
if (is_open)
return -EIO;
/* these are the ping pong buffers we'll use to transfer tx requests to usermode */
tx_box_count[0] = 0;
tx_box_count[1] = 0;
/* the current tx box is shared across all devices */
current_tx_box = 0;
is_open = 1;
return 0;
}
/* called when /dev/intrepid_netdevice is close -- delete any created interfaces */
/* called when /dev/intrepid_netdevice is closed -- delete any created interfaces */
static int intrepid_dev_release(struct inode *ip, struct file *fp)
{
int i;
@ -545,15 +672,11 @@ static int intrepid_dev_release(struct inode *ip, struct file *fp)
if (!is_open)
return -EIO;
tx_box_count[0] = 0;
tx_box_count[1] = 0;
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)
intrepid_remove_if(i);
intrepid_remove_can_if(i);
}
is_open = 0;
@ -566,9 +689,7 @@ static int intrepid_dev_release(struct inode *ip, struct file *fp)
static ssize_t intrepid_dev_read(struct file *fp, char *buffer, size_t len, loff_t *offset)
{
struct intrepid_pending_tx_info info;
struct net_device* dev;
struct intrepid_netdevice *ics;
int ret, i;
int ret;
if (len < sizeof(info))
return -EFAULT;
@ -578,44 +699,23 @@ static ssize_t intrepid_dev_read(struct file *fp, char *buffer, size_t len, loff
/* fill out the info for the user */
info.tx_box_index = current_tx_box;
info.count = tx_box_count[current_tx_box];
info.bytes = tx_box_bytes[current_tx_box];
ret = copy_to_user(buffer, &info, sizeof(info));
/* if we're full, pause the queue */
if (info.count == MAX_NUM_TX_MSGS)
{
for(i = 0 ; i < MAX_NET_DEVICES ; ++i)
{
dev = net_devices[i];
if (dev == NULL)
continue;
ics = netdev_priv(dev);
if (ics->is_stopped)
{
spin_lock_bh(&ics->lock);
netif_wake_queue(dev);
spin_unlock_bh(&ics->lock);
}
}
}
/* if we were full, unpause the queue */
intrepid_unpause_all_queues();
tx_box_count[current_tx_box] = 0;
tx_box_bytes[current_tx_box] = 0;
/* swap to the other buffer. once we unlock new tx messages will go to the new box */
if (current_tx_box == 0)
current_tx_box = 1;
else
current_tx_box = 0;
current_tx_box = current_tx_box == 0 ? 1 : 0;
spin_unlock_bh(&tx_box_lock);
if (ret != 0)
{
return -EFAULT;
}
return sizeof(info);
}
@ -649,16 +749,14 @@ static __init int intrepid_init(void)
/* this is the shared memory used to transfer between us and the user daemon */
shared_mem = vmalloc_user(SHARED_MEM_SIZE);
if (!shared_mem)
{
if (!shared_mem) {
ret = -ENOMEM;
goto exit;
}
/* make space for up to MAX_NET_DEVICES devices */
net_devices = kzalloc(sizeof(struct net_device*) * MAX_NET_DEVICES, GFP_KERNEL);
if (!net_devices)
{
if (!net_devices) {
ret = -ENOMEM;
goto free_shared_mem;
}
@ -670,27 +768,26 @@ static __init int intrepid_init(void)
* once some tx messages are ready */
init_waitqueue_head(&tx_wait);
/* this is used to arbitrate access to current_tx_box and tx_box_count */
spin_lock_init(&tx_box_lock);
/* parts of the shared memory for sending tx messages to user. we use ping pong
* buffers that get switched whenever we handle a read() */
tx_boxes[0] = GET_TX_BOX(0);
tx_boxes[1] = GET_TX_BOX(1);
tx_box_bytes[0] = 0;
tx_box_bytes[1] = 0;
tx_box_count[0] = 0;
tx_box_count[1] = 0;
current_tx_box = 0;
spin_lock_init(&tx_box_lock);
/* create /dev/intrepid_netdevice */
major_number = register_chrdev(0, INTREPID_DEVICE_NAME, &intrepid_fops);
if (major_number < 0)
{
if (major_number < 0) {
pr_alert("intrepid: failed to register major number, got %d\n",
major_number);
return -1;
}
intrepid_dev_class = class_create(THIS_MODULE, INTREPID_CLASS_NAME);
if (IS_ERR(intrepid_dev_class))
{
if (IS_ERR(intrepid_dev_class)) {
ret = PTR_ERR(intrepid_dev_class);
pr_alert("intrepid: failed to create device class, got %d\n", ret);
unregister_chrdev(major_number, INTREPID_DEVICE_NAME);
@ -699,8 +796,7 @@ static __init int intrepid_init(void)
intrepid_dev = device_create(intrepid_dev_class, NULL,
MKDEV(major_number, 0), NULL, INTREPID_DEVICE_NAME);
if(IS_ERR(intrepid_dev))
{
if (IS_ERR(intrepid_dev)) {
ret = PTR_ERR(intrepid_dev);
pr_alert("intrepid: failed to create device, got %d\n", ret);
class_destroy(intrepid_dev_class);
@ -730,19 +826,9 @@ static __exit void intrepid_exit(void)
class_destroy(intrepid_dev_class);
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)
{
// Kernel version 4.11.9 changed the destructor function to
// priv_destructor
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,11,9)
net_devices[i]->destructor = NULL; /* no dangling callbacks */
#else
net_devices[i]->priv_destructor = NULL;
#endif
intrepid_remove_if(i);
}
intrepid_remove_can_if(i);
}
kfree(net_devices);
@ -750,7 +836,6 @@ static __exit void intrepid_exit(void)
vfree(shared_mem);
shared_mem = NULL;
}
module_init(intrepid_init);

152
neomessage.h 100644
View File

@ -0,0 +1,152 @@
// This file is a subset of the neomessage.h found in libicsneo/include/communication/message
#ifndef __NEOMESSAGE_H_
#define __NEOMESSAGE_H_
#pragma pack(push, 1)
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4201) // nameless struct/union
#endif
typedef union {
struct {
uint32_t globalError : 1;
uint32_t transmitMessage : 1;
uint32_t extendedFrame : 1;
uint32_t remoteFrame : 1;
uint32_t crcError : 1;
uint32_t canErrorPassive : 1;
uint32_t incompleteFrame : 1;
uint32_t lostArbitration : 1;
uint32_t undefinedError : 1;
uint32_t canBusOff : 1;
uint32_t canErrorWarning : 1;
uint32_t canBusShortedPlus : 1;
uint32_t canBusShortedGround : 1;
uint32_t checksumError : 1;
uint32_t badMessageBitTimeError : 1;
uint32_t ifrData : 1;
uint32_t hardwareCommError : 1;
uint32_t expectedLengthError : 1;
uint32_t incomingNoMatch : 1;
uint32_t statusBreak : 1;
uint32_t avsiRecOverflow : 1;
uint32_t testTrigger : 1;
uint32_t audioComment : 1;
uint32_t gpsData : 1;
uint32_t analogDigitalInput : 1;
uint32_t textComment : 1;
uint32_t networkMessageType : 1;
uint32_t vsiTXUnderrun : 1;
uint32_t vsiIFRCRCBit : 1;
uint32_t initMessage : 1;
//uint32_t highSpeedMessage : 1; // Occupies the same space as flexraySecondStartupFrame
uint32_t flexraySecondStartupFrame : 1;
uint32_t extended : 1;
// ~~~ End of bitfield 1 ~~~
uint32_t hasValue : 1;
uint32_t valueIsBoolean : 1;
uint32_t highVoltage : 1;
uint32_t longMessage : 1;
uint32_t : 12;
uint32_t globalChange : 1;
uint32_t errorFrame : 1;
uint32_t : 2;
uint32_t endOfLongMessage : 1;
uint32_t linErrorRXBreakNotZero : 1;
uint32_t linErrorRXBreakTooShort : 1;
uint32_t linErrorRXSyncNot55 : 1;
uint32_t linErrorRXDataGreaterEight : 1;
uint32_t linErrorTXRXMismatch : 1;
uint32_t linErrorMessageIDParity : 1;
//isoFrameError
uint32_t linSyncFrameError : 1;
//isoOverflowError
uint32_t linIDFrameError : 1;
//isoParityError
uint32_t linSlaveByteError : 1;
uint32_t rxTimeoutError : 1;
uint32_t linNoSlaveData : 1;
// mostPacketData
// mostStatus
// mostLowLevel
// mostControlData
// mostMHPUserData
// mostMHPControlData
// mostI2SDump
// mostTooShort
// most50
// most150
// mostChangedParameter
// ethernetCRCError
// ethernetFrameTooShort
// ethernetFCSAvailable
// ~~~ End of bitfield 2 ~~~
//uint32_t linJustBreakSync : 1;
//uint32_t linSlaveDataTooShort : 1;
//uint32_t linOnlyUpdateSlaveTableOnce : 1;
uint32_t canfdESI : 1;
uint32_t canfdIDE : 1;
uint32_t canfdRTR : 1;
uint32_t canfdFDF : 1;
uint32_t canfdBRS : 1;
};
uint32_t statusBitfield[4];
} neomessage_statusbitfield_t;
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#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_CAN ((uint8_t)2)
#define ICSNEO_NETWORK_TYPE_LIN ((uint8_t)3)
#define ICSNEO_NETWORK_TYPE_FLEXRAY ((uint8_t)4)
#define ICSNEO_NETWORK_TYPE_MOST ((uint8_t)5)
#define ICSNEO_NETWORK_TYPE_ETHERNET ((uint8_t)6)
#define ICSNEO_NETWORK_TYPE_ANY ((uint8_t)0xFE) // Never actually set as type, but used as flag for filtering
#define ICSNEO_NETWORK_TYPE_OTHER ((uint8_t)0xFF)
typedef struct {
neomessage_statusbitfield_t status;
uint64_t timestamp;
uint64_t timestampReserved;
const uint8_t* data;
size_t length;
uint8_t header[4];
uint16_t netid;
uint8_t type;
uint8_t reserved[17];
} neomessage_t; // 72 bytes total
// Any time you add another neomessage_*_t type, make sure to add it to the static_asserts below!
typedef struct {
neomessage_statusbitfield_t status;
uint64_t timestamp;
uint64_t timestampReserved;
const uint8_t* data;
size_t length;
uint32_t arbid;
uint16_t netid;
uint8_t type;
uint8_t dlcOnWire;
uint8_t reserved[16];
} neomessage_can_t;
typedef struct {
neomessage_statusbitfield_t status;
uint64_t timestamp;
uint64_t timestampReserved;
const uint8_t* data;
size_t length;
uint8_t preemptionFlags;
uint8_t reservedHeader[3];
uint16_t netid;
uint8_t type;
uint8_t reserved[17];
} neomessage_eth_t;
#pragma pack(pop)
#endif

View File

@ -1,8 +0,0 @@
#!/bin/sh
sudo rmmod intrepid
sudo modprobe can
sudo modprobe can_raw
sudo modprobe can_dev
sudo insmod intrepid.ko