Compare commits

..

No commits in common. "master" and "v2.1.0" have entirely different histories.

6 changed files with 68 additions and 187 deletions

View File

@ -1,18 +1,3 @@
v3.1.2
Update libicsneo
Update copyright
Add scan-interval-ms parameter (Jorge Alejandro <jorge.a.alejandro@gmail.com>)
v3.1.1
Update libicsneo
v3.1.0
Update libicsneo
Update copyright
v3.0.0
Added Functionality for Ethernet
v2.1.0 v2.1.0
Update copyright date Update copyright date
Update to libicsneo v0.3.0 Update to libicsneo v0.3.0

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.2) cmake_minimum_required(VERSION 3.2)
project(libicsneo-socketcan-daemon VERSION 3.2.0) project(libicsneo-socketcan-daemon VERSION 2.1.0)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)

View File

@ -1,4 +1,4 @@
Copyright (c) 2016-2025 Intrepid Control Systems, Inc. Copyright (c) 2016-2022 Intrepid Control Systems, Inc.
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View File

@ -1,4 +1,4 @@
Version 3.1.2 Version 2.1.0
This is the usermode daemon for the Intrepid Control Systems SocketCAN support. This daemon requires that ```intrepid.ko``` is loaded on your system. This is the usermode daemon for the Intrepid Control Systems SocketCAN support. This daemon requires that ```intrepid.ko``` is loaded on your system.

View File

@ -30,24 +30,19 @@
#define LOGF(LVL, MSG, ...) do{if(runningAsDaemon) syslog(LVL, MSG, __VA_ARGS__); \ #define LOGF(LVL, MSG, ...) do{if(runningAsDaemon) syslog(LVL, MSG, __VA_ARGS__); \
else fprintf(stderr, MSG, __VA_ARGS__);}while(0) else fprintf(stderr, MSG, __VA_ARGS__);}while(0)
#define SIOCSADDCANIF 0x3001 #define SIOCSADDIF 0x3001
#define SIOCSADDETHIF 0x3002 #define SIOCSREMOVEIF 0x3002
#define SIOCSREMOVECANIF 0x3003 #define SIOCGSHAREDMEMSIZE 0x3003
#define SIOCSREMOVEETHIF 0x3004 #define SIOCSMSGSWRITTEN 0x3004
#define SIOCGSHAREDMEMSIZE 0x3005 #define SIOCGMAXIFACES 0x3005
#define SIOCSMSGSWRITTEN 0x3006 #define SIOCGVERSION 0x3006
#define SIOCGMAXIFACES 0x3007 #define SIOCGCLIENTVEROK 0x3007
#define SIOCGVERSION 0x3008
#define SIOCGCLIENTVEROK 0x3009
#define SIOCSBAUDRATE 0x300A
#define RX_BOX_SIZE (sharedMemSize / (maxInterfaces * 2)) #define RX_BOX_SIZE (sharedMemSize / (maxInterfaces * 2))
#define TX_BOX_SIZE (sharedMemSize / 4) #define TX_BOX_SIZE (sharedMemSize / 4)
#define GET_RX_BOX(DEVICE_INDEX) (reinterpret_cast<uint8_t*>(sharedMemory) + (RX_BOX_SIZE * DEVICE_INDEX)) #define GET_RX_BOX(DEVICE_INDEX) (reinterpret_cast<uint8_t*>(sharedMemory) + (RX_BOX_SIZE * DEVICE_INDEX))
#define GET_TX_BOX(INDEX) (reinterpret_cast<uint8_t*>(sharedMemory) + (sharedMemSize / 2) + (INDEX * TX_BOX_SIZE)) #define GET_TX_BOX(INDEX) (reinterpret_cast<uint8_t*>(sharedMemory) + (sharedMemSize / 2) + (INDEX * TX_BOX_SIZE))
#define DEFAULT_SCAN_INTERVAL_MS 1000
bool runningAsDaemon = false; bool runningAsDaemon = false;
int driver = 0; // /dev/intrepid_netdevice int driver = 0; // /dev/intrepid_netdevice
int driverMajor = 0; int driverMajor = 0;
@ -57,7 +52,6 @@ int maxInterfaces = 0; // From driver
int sharedMemSize = 0; // From driver int sharedMemSize = 0; // From driver
void* sharedMemory = nullptr; void* sharedMemory = nullptr;
std::string serialFilter; std::string serialFilter;
int scanIntervalMs = DEFAULT_SCAN_INTERVAL_MS;
std::atomic<bool> stopRunning(false); std::atomic<bool> stopRunning(false);
@ -69,16 +63,10 @@ struct intrepid_pending_tx_info {
class NetworkInterface { class NetworkInterface {
public: public:
NetworkInterface(const std::string& desiredName, icsneo::Network::Type device) : type(device), name(desiredName) { NetworkInterface(const std::string& desiredName) : name(desiredName) {
char ifname[IFALIASZ + 1] = {0}; char ifname[IFALIASZ + 1] = {0};
strncpy(ifname, name.c_str(), IFALIASZ); strncpy(ifname, name.c_str(), IFALIASZ);
kernelHandle = ioctl(driver, SIOCSADDIF, ifname);
if(device == icsneo::Network::Type::CAN) {
kernelHandle = ioctl(driver, SIOCSADDCANIF, ifname); // this will call the intrepid_dev_ioctl()
} else if(device == icsneo::Network::Type::Ethernet) {
kernelHandle = ioctl(driver, SIOCSADDETHIF, ifname); // this will call the intrepid_dev_ioctl()
}
if(openedSuccessfully()) { if(openedSuccessfully()) {
rxBox = GET_RX_BOX(kernelHandle); rxBox = GET_RX_BOX(kernelHandle);
rxBoxCurrentPosition = rxBox; rxBoxCurrentPosition = rxBox;
@ -86,36 +74,12 @@ public:
} }
~NetworkInterface() { ~NetworkInterface() {
if(openedSuccessfully()) { if(openedSuccessfully()) {
int res = 0;
LOGF(LOG_DEBUG, "Removing device %s with handle %d\n", name.c_str(), kernelHandle); LOGF(LOG_DEBUG, "Removing device %s with handle %d\n", name.c_str(), kernelHandle);
if(type == icsneo::Network::Type::CAN) { int res = ioctl(driver, SIOCSREMOVEIF, kernelHandle);
res = ioctl(driver, SIOCSREMOVECANIF, kernelHandle);
} else if(type == icsneo::Network::Type::Ethernet) {
res = ioctl(driver, SIOCSREMOVEETHIF, kernelHandle);
}
LOGF(LOG_DEBUG, "Removed device %s with handle %d, result %d\n", name.c_str(), kernelHandle, res); LOGF(LOG_DEBUG, "Removed device %s with handle %d, result %d\n", name.c_str(), kernelHandle, res);
} else } else
LOG(LOG_DEBUG, "Removing interface which was not opened successfully\n"); LOG(LOG_DEBUG, "Removing interface which was not opened successfully\n");
} }
bool reportBaudrates(int64_t baudrate, int64_t fd_baudrate) {
struct baudrate_info {
int handle;
int64_t baudrates[2];
} info;
info.handle = kernelHandle;
info.baudrates[0] = baudrate;
/* set fd baudrate to zero if equal to baudrate
* this will disable fd mode in kernel */
info.baudrates[1] = (fd_baudrate==baudrate)?0:fd_baudrate;
if (ioctl(driver, SIOCSBAUDRATE, &info) != 0) {
LOGF(LOG_INFO, "Unable to set baudrate for device %s\n", name.c_str());
return false;
}
return true;
}
NetworkInterface(const NetworkInterface&) = delete; NetworkInterface(const NetworkInterface&) = delete;
NetworkInterface& operator =(const NetworkInterface&) = delete; NetworkInterface& operator =(const NetworkInterface&) = delete;
@ -124,18 +88,14 @@ public:
const std::string& getName() const { return name; } const std::string& getName() const { return name; }
uint8_t* getRxBox() { return rxBox; } uint8_t* getRxBox() { return rxBox; }
const uint8_t* getRxBox() const { return rxBox; } const uint8_t* getRxBox() const { return rxBox; }
void addReceivedMessageToQueue(const std::shared_ptr<icsneo::CANMessage>& msg) {
template<typename T>
void addReceivedMessageToQueue(const std::shared_ptr<icsneo::Frame>& msg) {
const auto neomessageGeneric = icsneo::CreateNeoMessage(msg); const auto neomessageGeneric = icsneo::CreateNeoMessage(msg);
if(neomessageGeneric.messageType != neomessagetype_t(icsneo::Message::Type::Frame)) { if (neomessageGeneric.messageType != neomessagetype_t(icsneo::Message::Type::Frame)) {
LOG(LOG_DEBUG, "could not create a neomessage_can_t\n"); LOG(LOG_DEBUG, "could not create a neomessage_can_t\n");
return; return;
} }
if(msg->network.getType() == icsneo::Network::Type::CAN || msg->network.getType() == icsneo::Network::Type::Ethernet) { const auto& neomessage = *reinterpret_cast<const neomessage_can_t*>(&neomessageGeneric);
const auto& neomessage = *reinterpret_cast<const T*>(&neomessageGeneric);
size_t bytesNeeded = sizeof(neomessage) + neomessage.length; size_t bytesNeeded = sizeof(neomessage) + neomessage.length;
std::lock_guard<std::mutex> lg(rxBoxLock); std::lock_guard<std::mutex> lg(rxBoxLock);
@ -156,10 +116,8 @@ public:
rxBoxCurrentPosition = rxBox; rxBoxCurrentPosition = rxBox;
rxBoxMessageCount = 0; rxBoxMessageCount = 0;
} }
}
private: private:
icsneo::Network::Type type;
std::string name; std::string name;
int kernelHandle = -1; int kernelHandle = -1;
std::mutex rxBoxLock; std::mutex rxBoxLock;
@ -222,7 +180,7 @@ std::string sanitizeInterfaceName(std::string str) {
void header() { void header() {
std::cout << "The libicsneo SocketCAN Usermode Daemon\n"; std::cout << "The libicsneo SocketCAN Usermode Daemon\n";
std::cout << "Copyright Intrepid Control Systems, Inc. 2025\n\n"; std::cout << "Copyright Intrepid Control Systems, Inc. 2019\n\n";
std::cout << "Daemon v"; std::cout << "Daemon v";
std::cout << (int)ICSNEO_SOCKETCAN_BUILD_MAJOR << '.' << (int)ICSNEO_SOCKETCAN_BUILD_MINOR << '.' << (int)ICSNEO_SOCKETCAN_BUILD_PATCH; std::cout << (int)ICSNEO_SOCKETCAN_BUILD_MAJOR << '.' << (int)ICSNEO_SOCKETCAN_BUILD_MINOR << '.' << (int)ICSNEO_SOCKETCAN_BUILD_PATCH;
if(ICSNEO_SOCKETCAN_BUILD_METADATA[0] != '\0') if(ICSNEO_SOCKETCAN_BUILD_METADATA[0] != '\0')
@ -239,14 +197,13 @@ void header() {
void usage(std::string executableName) { void usage(std::string executableName) {
std::cerr << "The libicsneo SocketCAN Usermode Daemon\n"; std::cerr << "The libicsneo SocketCAN Usermode Daemon\n";
std::cerr << "Copyright 2019-2025 Intrepid Control Systems, Inc.\n\n"; std::cerr << "Copyright 2019-2022 Intrepid Control Systems, Inc.\n\n";
std::cerr << "Usage: " << executableName << " [option]\n\n"; std::cerr << "Usage: " << executableName << " [option]\n\n";
std::cerr << "Options:\n"; std::cerr << "Options:\n";
std::cerr << "\t-d, --daemon\t\t\tRun as a daemon in the background\n"; std::cerr << "\t-d, --daemon\t\tRun as a daemon in the background\n";
std::cerr << "\t-h, -?, --help, --usage\t\t\tShow this help page\n"; std::cerr << "\t-h, -?, --help, --usage\t\tShow this help page\n";
std::cerr << "\t --devices\t\t\tList supported devices\n"; std::cerr << "\t --devices\t\tList supported devices\n";
std::cerr << "\t --filter <serial>\t\tOnly connect to devices with serial\n\t\t\t\t\t\tnumbers starting with this filter\n"; std::cerr << "\t --filter <serial>\tOnly connect to devices with serial\n\t\t\t\t\tnumbers starting with this filter\n";
std::cerr << "\t --scan-interval-ms <interval>\tDevice scan interval in ms\n\t\t\t\t\t\tIf 0, only a single scan is performed\n";
} }
void terminateSignal(int signal) { void terminateSignal(int signal) {
@ -289,28 +246,27 @@ void searchForDevices() {
continue; continue;
} }
// Get the supported networks // Get the supported CAN networks
auto supportedNetworks = newDevice.device->getSupportedRXNetworks(); auto supportedNetworks = newDevice.device->getSupportedRXNetworks();
supportedNetworks.erase(std::remove_if(supportedNetworks.begin(), supportedNetworks.end(), [](const icsneo::Network& net) -> bool { supportedNetworks.erase(std::remove_if(supportedNetworks.begin(), supportedNetworks.end(), [](const icsneo::Network& net) -> bool {
return net.getType() != icsneo::Network::Type::CAN && net.getType() != icsneo::Network::Type::Ethernet; return net.getType() != icsneo::Network::Type::CAN;// Only want CAN networks
}), supportedNetworks.end()); }), supportedNetworks.end());
if(supportedNetworks.empty()) { if(supportedNetworks.empty()) {
if(firstTimeFailedToOpen) { if(firstTimeFailedToOpen) {
LOGF(LOG_INFO, "%s has no supported networks\n", newDevice.device->describe().c_str()); LOGF(LOG_INFO, "%s has no supported CAN networks\n", newDevice.device->describe().c_str());
failedToOpen.push_back(serial); failedToOpen.push_back(serial);
} }
continue; continue;
} }
// Create a network interface for each network // Create a network interface for each CAN network
for(const auto& net : supportedNetworks) { for(const auto& net : supportedNetworks) {
std::stringstream ss; std::stringstream ss;
ss << sanitizeInterfaceName(icsneo::Network::GetNetIDString(net.getNetID())) << "_" << serial; ss << sanitizeInterfaceName(icsneo::Network::GetNetIDString(net.getNetID())) << "_" << serial;
std::string interfaceName(ss.str()); std::string interfaceName(ss.str());
if(firstTimeFailedToOpen) if(firstTimeFailedToOpen)
LOGF(LOG_INFO, "Creating network interface %s\n", interfaceName.c_str()); LOGF(LOG_INFO, "Creating network interface %s\n", interfaceName.c_str());
newDevice.interfaces[net.getNetID()] = std::make_shared<NetworkInterface>(interfaceName);
newDevice.interfaces[net.getNetID()] = std::make_shared<NetworkInterface>(interfaceName, net.getType());
LOGF(LOG_INFO, "Created network interface %s\n", interfaceName.c_str()); LOGF(LOG_INFO, "Created network interface %s\n", interfaceName.c_str());
} }
bool failedToCreateNetworkInterfaces = false; bool failedToCreateNetworkInterfaces = false;
@ -327,21 +283,10 @@ void searchForDevices() {
} }
continue; continue;
} }
if (driverMinor > 0) {
for(const auto& net : supportedNetworks) {
if (net.getType() != icsneo::Network::Type::CAN)
continue;
newDevice.interfaces[net.getNetID()]->reportBaudrates(
newDevice.device->settings->getBaudrateFor(net.getNetID()),
newDevice.device->settings->getFDBaudrateFor(net.getNetID())
);
}
}
// Create rx listener // Create rx listener
newDevice.device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([serial](std::shared_ptr<icsneo::Message> message) { newDevice.device->addMessageCallback(icsneo::CANMessageCallback([serial](std::shared_ptr<icsneo::Message> message) {
const auto frame = std::static_pointer_cast<icsneo::Frame>(message); auto canMessage = std::static_pointer_cast<icsneo::CANMessage>(message);
const auto messageType = frame->network.getType();
const OpenDevice* openDevice = nullptr; const OpenDevice* openDevice = nullptr;
std::lock_guard<std::mutex> lg(openDevicesMutex); std::lock_guard<std::mutex> lg(openDevicesMutex);
for(const auto& dev : openDevices) { for(const auto& dev : openDevices) {
@ -350,17 +295,13 @@ void searchForDevices() {
break; break;
} }
} }
if(frame->type != icsneo::Message::Type::Frame) { if(!openDevice) {
LOG(LOG_ERR, "Dropping message: received invalid message type, expected RawMessage\n"); LOG(LOG_ERR, "Dropping message, no open device\n");
return; return;
} }
if(messageType == icsneo::Network::Type::CAN) { // todo might throw
openDevice->interfaces.at(frame->network.getNetID())->addReceivedMessageToQueue<neomessage_can_t>(frame); openDevice->interfaces.at(canMessage->network.getNetID())->addReceivedMessageToQueue(canMessage);
} else if(messageType == icsneo::Network::Type::Ethernet) {
openDevice->interfaces.at(frame->network.getNetID())->addReceivedMessageToQueue<neomessage_eth_t>(frame);
} else
LOG(LOG_ERR, "Dropping message, only CAN and Ethernet are currently supported\n");
})); }));
LOGF(LOG_INFO, "%s connected\n", newDevice.device->describe().c_str()); LOGF(LOG_INFO, "%s connected\n", newDevice.device->describe().c_str());
@ -412,10 +353,7 @@ void searchForDevices() {
void deviceSearchThread() { void deviceSearchThread() {
while(!stopRunning) { while(!stopRunning) {
searchForDevices(); searchForDevices();
if(scanIntervalMs == 0) { std::this_thread::sleep_for(std::chrono::milliseconds(1000));
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(scanIntervalMs));
} }
} }
@ -436,21 +374,6 @@ int main(int argc, char** argv) {
} else if(arg == "--filter" && i + 1 <= argc) { } else if(arg == "--filter" && i + 1 <= argc) {
serialFilter = argv[++i]; serialFilter = argv[++i];
transform(serialFilter.begin(), serialFilter.end(), serialFilter.begin(), ::toupper); transform(serialFilter.begin(), serialFilter.end(), serialFilter.begin(), ::toupper);
} else if(arg == "--scan-interval-ms" && i + 1 <= argc) {
try {
scanIntervalMs = std::stoi(argv[++i]);
} catch (const std::invalid_argument& e) {
std::cerr << "Invalid input for scan-interval-ms\n";
return EX_USAGE;
} catch (const std::out_of_range& e) {
std::cerr << "Out of range input for scan-interval-ms\n";
return EX_USAGE;
}
if(scanIntervalMs < 0) {
std::cerr << "Invalid input for scan-interval-ms\n";
return EX_USAGE;
}
} else { } else {
usage(argv[0]); usage(argv[0]);
return EX_USAGE; return EX_USAGE;
@ -494,7 +417,7 @@ int main(int argc, char** argv) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
std::cout << "Driver v" << driverMajor << '.' << driverMinor << '.' << driverPatch << "\n\n"; std::cout << "Driver v" << driverMajor << '.' << driverMinor << '.' << driverPatch << "\n\n";
if(driverMajor > 3) { if(driverMajor > 2) {
LOG(LOG_ERR, "This version of the usermode daemon is too old to work with this driver\nPlease ensure that both the usermode daemon and kernel driver are up to date\n"); LOG(LOG_ERR, "This version of the usermode daemon is too old to work with this driver\nPlease ensure that both the usermode daemon and kernel driver are up to date\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -554,7 +477,7 @@ int main(int argc, char** argv) {
// Call read() to find out which box they're in and how many // Call read() to find out which box they're in and how many
struct intrepid_pending_tx_info info; struct intrepid_pending_tx_info info;
ssize_t r = read(driver, &info, sizeof(info)); ssize_t r = read(driver, &info, sizeof(info));
if(r == -1) { if (r == -1) {
LOGF(LOG_ERR, "Error waiting for tx messages: %s\n", strerror(errno)); LOGF(LOG_ERR, "Error waiting for tx messages: %s\n", strerror(errno));
stopRunning = true; stopRunning = true;
break; break;
@ -562,33 +485,6 @@ int main(int argc, char** argv) {
LOGF(LOG_ERR, "Unexpected number of bytes read, expected %d got %d\n", (int)sizeof(info), (int)r); LOGF(LOG_ERR, "Unexpected number of bytes read, expected %d got %d\n", (int)sizeof(info), (int)r);
stopRunning = true; stopRunning = true;
break; break;
} else if (info.tx_box_index < 0) {
// Baudrate changed in kernel
int dev_idx = -(info.tx_box_index + 1);
LOGF(LOG_INFO, "Baudrate change, device %d, baudrate %d fd_baudrate %ld\n",
dev_idx, info.count, info.bytes);
/* fd baudrate is zero if fd mode is disabled in kernel
* set fd baudrate equal to baudrate */
if (info.bytes == 0) {
info.bytes = info.count;
}
for(auto& dev : openDevices) {
for(auto& netifPair : dev.interfaces) {
auto netid = netifPair.first;
if(netifPair.second->getKernelHandle() != dev_idx)
continue;
if (! dev.device->settings->setBaudrateFor(netid, info.count) ) {
LOGF(LOG_ERR, "Unable to set baudrate for device %s\n",
netifPair.second->getName().c_str());
} else if (! dev.device->settings->setFDBaudrateFor(netid, info.bytes)) {
LOGF(LOG_ERR, "Unable to set fd baudrate for device %s\n",
netifPair.second->getName().c_str());
} else if (! dev.device->settings->apply()) {
LOGF(LOG_ERR, "Unable to apply settings for device %s\n",
netifPair.second->getName().c_str());
}
}
}
} else { } else {
// Send! // Send!
uint8_t* currentPosition = GET_TX_BOX(info.tx_box_index); uint8_t* currentPosition = GET_TX_BOX(info.tx_box_index);
@ -598,8 +494,8 @@ int main(int argc, char** argv) {
msg->data = currentPosition; msg->data = currentPosition;
currentPosition += msg->length; currentPosition += msg->length;
if(msg->type != neonettype_t(icsneo::Network::Type::CAN) && msg->type != neonettype_t(icsneo::Network::Type::Ethernet)) { if(msg->type != neonettype_t(icsneo::Network::Type::CAN)) {
LOG(LOG_ERR, "Message dropped, kernel sent a non-CAN/Ethernet message\n"); LOG(LOG_ERR, "Message dropped, kernel sent a non-CAN message\n");
continue; continue;
} }

@ -1 +1 @@
Subproject commit deabc2cff438819c3a18a6325ba6fce080d3882c Subproject commit 0ff12300f34be54ec7d3380a4ab6693a7d7c3fb1