mirror of
https://github.com/intrepidcs/icsscand.git
synced 2026-09-21 16:48:21 +02:00
Compare commits
8
Commits
89a58ff40c
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a38ebda34 | ||
|
|
4801a60598 | ||
|
|
bd37f9f8a1 | ||
|
|
5612f583b8 | ||
|
|
a15b71d970 | ||
|
|
9f2896cfc0 | ||
|
|
2838f4488c | ||
|
|
8c7f306771 |
@@ -1,3 +1,4 @@
|
|||||||
[submodule "third-party/libicsneo"]
|
[submodule "third-party/libicsneo"]
|
||||||
path = third-party/libicsneo
|
path = third-party/libicsneo
|
||||||
url = https://github.com/intrepidcs/libicsneo.git
|
url = https://github.com/intrepidcs/libicsneo.git
|
||||||
|
branch = master
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.2)
|
cmake_minimum_required(VERSION 3.2)
|
||||||
project(libicsneo-socketcan-daemon VERSION 3.3.0)
|
project(libicsneo-socketcan-daemon VERSION 3.2.0)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
@@ -27,5 +27,5 @@ include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
|
|||||||
|
|
||||||
add_subdirectory("third-party/libicsneo")
|
add_subdirectory("third-party/libicsneo")
|
||||||
|
|
||||||
add_executable(libicsneo-socketcan-daemon src/main.cpp src/netlink.c)
|
add_executable(libicsneo-socketcan-daemon src/main.cpp)
|
||||||
target_link_libraries(libicsneo-socketcan-daemon icsneocpp)
|
target_link_libraries(libicsneo-socketcan-daemon icsneocpp)
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
# RPC
|
||||||
|
|
||||||
|
icsscand contains an RPC endpoint that can be used to control the libicsneo
|
||||||
|
instance within the daemon. To enable the RPC channel, add `--fifo-path <path>`
|
||||||
|
during launching. The provided path will be used to open a named FIFO, the path
|
||||||
|
should not exist prior to launching icsscand.
|
||||||
|
|
||||||
|
## lock_networks
|
||||||
|
|
||||||
|
Invokes `icsneo::Device::lockNetworks()` with the provided space separated
|
||||||
|
arguments and writes the results of the call to the provided client FIFO. This
|
||||||
|
call in non-blocking, with the return type indicating that the request was sent
|
||||||
|
to the device (it does not indicate that the network was successfully locked).
|
||||||
|
Use `get_network_mutex_status` to check the status of the lock request.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
- Request: `<api version> lock_networks <device serial> <netid integers, comma separated> <priority> <ttl, ms> <lock type> <client fifo path>`
|
||||||
|
- Response: `<0 or 1, with 0 indicating an error and 1 indicating success>`
|
||||||
|
- On error, check the icsscand logs for more detailed information
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
- API version: 1
|
||||||
|
- Service serial: ON0123
|
||||||
|
- Networks: `ETHERNET_01` & `ETHERNET_02`
|
||||||
|
- See `icsneo::Network::NetID` for values
|
||||||
|
- Priority: 2
|
||||||
|
- TTL (in ms): 1s
|
||||||
|
- Opened client FIFO path: `/tmp/tmp.AYkIg0b3np`
|
||||||
|
- This FIFO must be opened prior to invoking the RPC
|
||||||
|
|
||||||
|
`1 lock_networks ON0123 93,520 2 1000 /tmp/tmp.AYkIg0b3np`
|
||||||
|
|
||||||
|
## unlock_networks
|
||||||
|
|
||||||
|
Invokes `icsneo::Device::unlockNetworks()` with the provided space separated
|
||||||
|
arguments and writes the results of the call to the provided client FIFO.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
- Request: `<api version> unlock_networks <device serial> <netid integers, comma separated> <client fifo path>`
|
||||||
|
- Response: `<0 or 1, with 0 indicating an error and 1 indicating success>`
|
||||||
|
- On error, check the icsscand logs for more detailed information
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
- API version: 1
|
||||||
|
- Service serial: ON0123
|
||||||
|
- Networks: `ETHERNET_01` & `ETHERNET_02`
|
||||||
|
- See `icsneo::Network::NetID` for values
|
||||||
|
- Opened client FIFO path: `/tmp/tmp.KXY8SCXtux`
|
||||||
|
- This FIFO must be opened prior to invoking the RPC
|
||||||
|
|
||||||
|
`1 lock_networks ON0123 93,520 /tmp/tmp.KXY8SCXtux`
|
||||||
|
|
||||||
|
## get_network_mutex_status
|
||||||
|
|
||||||
|
Invokes `icsneo::Device::getNetworkMutexStatus()` with the provided space separated
|
||||||
|
arguments and writes the results of the call to the provided client FIFO.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
- Request: `<api version> get_network_mutex_status <device serial> <netid integer> <client fifo path>`
|
||||||
|
- Response (one of):
|
||||||
|
- `0`, error, check the icsscand logs for more detailed information
|
||||||
|
- `1 <owner client id> <type> <priority> <ttl, ms> <netid integers, comma separated> <event>`
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
- API version: 1
|
||||||
|
- Service serial: ON0123
|
||||||
|
- Network: `ETHERNET_01`
|
||||||
|
- See `icsneo::Network::NetID` for values
|
||||||
|
- Opened client FIFO path: `/tmp/tmp.4huaosZjhA`
|
||||||
|
- This FIFO must be opened prior to invoking the RPC
|
||||||
|
|
||||||
|
`1 get_network_mutex_status ON0123 93 /tmp/tmp.4huaosZjhA`
|
||||||
|
|
||||||
|
## get_serials
|
||||||
|
|
||||||
|
Returns a space separated list of devices serial numbers that isscand has open.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
- Request: `<api version> get_serials <client fifo path>`
|
||||||
|
- Response: `<0 or 1, with 0 indicating an error and 1 indicating success> [serial]...`
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
- API version: 1
|
||||||
|
- Opened client FIFO path: `/tmp/tmp.bBcUh5obRK`
|
||||||
|
- This FIFO must be opened prior to invoking the RPC
|
||||||
|
|
||||||
|
`1 get_serials /tmp/tmp.bBcUh5obRK`
|
||||||
+395
-429
@@ -17,7 +17,8 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <linux/if.h>
|
#include <linux/if.h>
|
||||||
#include <linux/can/netlink.h>
|
#include <sys/eventfd.h>
|
||||||
|
#include <poll.h>
|
||||||
|
|
||||||
#include <icsneo/icsneocpp.h>
|
#include <icsneo/icsneocpp.h>
|
||||||
#include <icsneo/communication/message/neomessage.h>
|
#include <icsneo/communication/message/neomessage.h>
|
||||||
@@ -26,8 +27,6 @@
|
|||||||
#include <icsneo/communication/message/callback/canmessagecallback.h>
|
#include <icsneo/communication/message/callback/canmessagecallback.h>
|
||||||
#include <generated/buildinfo.h>
|
#include <generated/buildinfo.h>
|
||||||
|
|
||||||
#include "netlink.h"
|
|
||||||
|
|
||||||
#define LOG(LVL, MSG) do{if(runningAsDaemon) syslog(LVL, MSG); \
|
#define LOG(LVL, MSG) do{if(runningAsDaemon) syslog(LVL, MSG); \
|
||||||
else fprintf(stderr, MSG);}while(0)
|
else fprintf(stderr, MSG);}while(0)
|
||||||
#define LOGF(LVL, MSG, ...) do{if(runningAsDaemon) syslog(LVL, MSG, __VA_ARGS__); \
|
#define LOGF(LVL, MSG, ...) do{if(runningAsDaemon) syslog(LVL, MSG, __VA_ARGS__); \
|
||||||
@@ -43,8 +42,6 @@
|
|||||||
#define SIOCGVERSION 0x3008
|
#define SIOCGVERSION 0x3008
|
||||||
#define SIOCGCLIENTVEROK 0x3009
|
#define SIOCGCLIENTVEROK 0x3009
|
||||||
#define SIOCSBAUDRATE 0x300A
|
#define SIOCSBAUDRATE 0x300A
|
||||||
#define SIOCSERRCOUNT 0x300B
|
|
||||||
#define SIOCSIFSETTINGS 0x300C
|
|
||||||
|
|
||||||
#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)
|
||||||
@@ -63,6 +60,7 @@ int sharedMemSize = 0; // From driver
|
|||||||
void* sharedMemory = nullptr;
|
void* sharedMemory = nullptr;
|
||||||
std::string serialFilter;
|
std::string serialFilter;
|
||||||
int scanIntervalMs = DEFAULT_SCAN_INTERVAL_MS;
|
int scanIntervalMs = DEFAULT_SCAN_INTERVAL_MS;
|
||||||
|
std::string fifoPath;
|
||||||
|
|
||||||
std::atomic<bool> stopRunning(false);
|
std::atomic<bool> stopRunning(false);
|
||||||
|
|
||||||
@@ -72,144 +70,19 @@ struct intrepid_pending_tx_info {
|
|||||||
size_t bytes;
|
size_t bytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define ICS_MAGIC 0x49435343 // ICSC
|
|
||||||
|
|
||||||
struct add_can_if_info {
|
|
||||||
char alias[IFALIASZ];
|
|
||||||
__u32 magic;
|
|
||||||
__u32 ctrl_mode;
|
|
||||||
struct can_clock clock;
|
|
||||||
struct can_bittiming_const bittiming_const;
|
|
||||||
struct can_bittiming_const data_bittiming_const;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct can_err_report {
|
|
||||||
int device;
|
|
||||||
enum can_state state;
|
|
||||||
struct can_berr_counter err_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct can_dev_settings {
|
|
||||||
int device;
|
|
||||||
struct can_bittiming bittiming;
|
|
||||||
struct can_bittiming data_bittiming;
|
|
||||||
__u32 ctrl_mode;
|
|
||||||
bool termination;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct can_clock clock_bxcan = {
|
|
||||||
.freq = 80000000,
|
|
||||||
};
|
|
||||||
static struct can_bittiming_const bittiming_const_bxcan = {
|
|
||||||
.name = "bxcan-31X",
|
|
||||||
.tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */
|
|
||||||
.tseg1_max = 256,
|
|
||||||
.tseg2_min = 2, /* Time segment 2 = phase_seg2 */
|
|
||||||
.tseg2_max = 128,
|
|
||||||
.sjw_max = 128,
|
|
||||||
.brp_min = 1,
|
|
||||||
.brp_max = 512,
|
|
||||||
.brp_inc = 1,
|
|
||||||
};
|
|
||||||
static struct can_bittiming_const data_bittiming_const_bxcan = {
|
|
||||||
.name = "bxcan-31X",
|
|
||||||
.tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */
|
|
||||||
.tseg1_max = 32,
|
|
||||||
.tseg2_min = 1, /* Time segment 2 = phase_seg2 */
|
|
||||||
.tseg2_max = 16,
|
|
||||||
.sjw_max = 16,
|
|
||||||
.brp_min = 1,
|
|
||||||
.brp_max = 32,
|
|
||||||
.brp_inc = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct can_clock clock_dspic = {
|
|
||||||
.freq = 40000000,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct can_bittiming_const bittiming_const_dspic = {
|
|
||||||
.name = "dspic33fj",
|
|
||||||
.tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */
|
|
||||||
.tseg1_max = 8,
|
|
||||||
.tseg2_min = 1, /* Time segment 2 = phase_seg2 */
|
|
||||||
.tseg2_max = 8,
|
|
||||||
.sjw_max = 4,
|
|
||||||
.brp_min = 2,
|
|
||||||
.brp_max = 128,
|
|
||||||
.brp_inc = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct can_dev_info {
|
|
||||||
devicetype_t device_type;
|
|
||||||
struct can_clock *clock;
|
|
||||||
struct can_bittiming_const *bittiming_const;
|
|
||||||
struct can_bittiming_const *data_bittiming_const;
|
|
||||||
} dev_infos[] = {
|
|
||||||
{ icsneo::DeviceType::Enum::ECU_AVB, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::RADMars, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::VCAN4_1, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::RADPluto, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::VCAN4_2EL, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::FIRE3, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::RADJupiter, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::VCAN4_IND, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::RADGigastar, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::RED2, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::RAD_A2B, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::RADEpsilon, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::RADMoon3, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::RADComet, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::FIRE3_FlexRay, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::VCAN4_4, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::VCAN4_2, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::FIRE2, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::RADGalaxy, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::RADStar2, &clock_bxcan, &bittiming_const_bxcan, &data_bittiming_const_bxcan },
|
|
||||||
{ icsneo::DeviceType::Enum::FIRE, &clock_dspic, &bittiming_const_dspic, NULL },
|
|
||||||
{ icsneo::DeviceType::Enum::VCAN3, &clock_dspic, &bittiming_const_dspic, NULL },
|
|
||||||
{ icsneo::DeviceType::Enum::RED, &clock_dspic, &bittiming_const_dspic, NULL },
|
|
||||||
};
|
|
||||||
|
|
||||||
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
|
|
||||||
|
|
||||||
static struct can_dev_info* get_infos_for_device(devicetype_t device_type)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(dev_infos); ++i) {
|
|
||||||
if (dev_infos[i].device_type == device_type) {
|
|
||||||
return &dev_infos[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
class NetworkInterface {
|
class NetworkInterface {
|
||||||
public:
|
public:
|
||||||
NetworkInterface(const std::string& desiredName, icsneo::Network::Type device, devicetype_t device_type)
|
NetworkInterface(const std::string& desiredName, icsneo::Network::Type device) : type(device), name(desiredName) {
|
||||||
: type(device), name(desiredName) {
|
char ifname[IFALIASZ + 1] = {0};
|
||||||
struct add_can_if_info info = {
|
strncpy(ifname, name.c_str(), IFALIASZ);
|
||||||
.magic = ICS_MAGIC,
|
|
||||||
};
|
|
||||||
strncpy(info.alias, name.c_str(), IFALIASZ);
|
|
||||||
|
|
||||||
if(device == icsneo::Network::Type::CAN) {
|
if(device == icsneo::Network::Type::CAN) {
|
||||||
struct can_dev_info *dev_info = get_infos_for_device(device_type);
|
kernelHandle = ioctl(driver, SIOCSADDCANIF, ifname); // this will call the intrepid_dev_ioctl()
|
||||||
if (dev_info) {
|
|
||||||
info.ctrl_mode = CAN_CTRLMODE_BERR_REPORTING;
|
|
||||||
info.clock = *(dev_info->clock);
|
|
||||||
info.bittiming_const = *(dev_info->bittiming_const);
|
|
||||||
if (dev_info->data_bittiming_const) {
|
|
||||||
info.ctrl_mode |= CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO;
|
|
||||||
info.data_bittiming_const = *(dev_info->data_bittiming_const);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
kernelHandle = ioctl(driver, SIOCSADDCANIF, &info); // this will call the intrepid_dev_ioctl()
|
|
||||||
} else if(device == icsneo::Network::Type::Ethernet) {
|
} else if(device == icsneo::Network::Type::Ethernet) {
|
||||||
kernelHandle = ioctl(driver, SIOCSADDETHIF, &info.alias); // this will call the intrepid_dev_ioctl()
|
kernelHandle = ioctl(driver, SIOCSADDETHIF, ifname); // this will call the intrepid_dev_ioctl()
|
||||||
}
|
}
|
||||||
|
|
||||||
if(openedSuccessfully()) {
|
if(openedSuccessfully()) {
|
||||||
ifindex = ioctl(driver, SIOCGIFINDEX, kernelHandle);
|
|
||||||
LOGF(LOG_INFO, "Ifindex for device %s is %d\n", name.c_str(), ifindex);
|
|
||||||
rxBox = GET_RX_BOX(kernelHandle);
|
rxBox = GET_RX_BOX(kernelHandle);
|
||||||
rxBoxCurrentPosition = rxBox;
|
rxBoxCurrentPosition = rxBox;
|
||||||
}
|
}
|
||||||
@@ -228,35 +101,33 @@ public:
|
|||||||
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;
|
||||||
|
|
||||||
bool openedSuccessfully() const { return kernelHandle >= 0; }
|
bool openedSuccessfully() const { return kernelHandle >= 0; }
|
||||||
int getKernelHandle() const { return kernelHandle; }
|
int getKernelHandle() const { return kernelHandle; }
|
||||||
int getIfIndex() const { return ifindex; }
|
|
||||||
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 reportErrorCount(const std::shared_ptr<icsneo::CANErrorCountMessage>& msg) {
|
|
||||||
LOGF(LOG_INFO, "%s CAN error count tx:%d rx:%d busoff:%d\n",
|
|
||||||
name.c_str(), msg->transmitErrorCount, msg->receiveErrorCount,
|
|
||||||
msg->busOff);
|
|
||||||
struct can_err_report err = {
|
|
||||||
.device = kernelHandle,
|
|
||||||
.state = (msg->busOff)?CAN_STATE_BUS_OFF:CAN_STATE_ERROR_ACTIVE,
|
|
||||||
.err_count = {
|
|
||||||
.txerr = msg->transmitErrorCount,
|
|
||||||
.rxerr = msg->receiveErrorCount,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if(ioctl(driver, SIOCSERRCOUNT, &err) < 0) {
|
|
||||||
LOGF(LOG_DEBUG, "error report ioctl failed %d\n", kernelHandle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void addReceivedMessageToQueue(const std::shared_ptr<icsneo::Frame>& msg) {
|
void addReceivedMessageToQueue(const std::shared_ptr<icsneo::Frame>& msg) {
|
||||||
const auto neomessageGeneric = icsneo::CreateNeoMessage(msg);
|
const auto neomessageGeneric = icsneo::CreateNeoMessage(msg);
|
||||||
@@ -290,209 +161,14 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_bittiming(struct can_bittiming *bt)
|
|
||||||
{
|
|
||||||
|
|
||||||
struct can_clock clock = {
|
|
||||||
.freq = 80000000,
|
|
||||||
};
|
|
||||||
__u64 v64 = (__u64)bt->brp * 1000 * 1000 * 1000;
|
|
||||||
v64 = v64 / clock.freq;
|
|
||||||
bt->tq = (__u32)v64;
|
|
||||||
|
|
||||||
__u32 tseg = 1 + bt->prop_seg + bt->phase_seg1 + bt->phase_seg2;
|
|
||||||
bt->bitrate = clock.freq / (bt->brp * tseg);
|
|
||||||
bt->sample_point = 1000 * (tseg - bt->phase_seg2) / tseg;
|
|
||||||
|
|
||||||
bt->sjw = std::max(1U, std::min(bt->phase_seg1, bt->phase_seg2 / 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void storeCanSettings(const CAN_SETTINGS *can, const CANFD_SETTINGS *canfd, bool termination) {
|
|
||||||
LOGF(LOG_INFO, "Baudrate:%d TqSeg1:%d TqSeg2:%d TqProp:%d TqSync:%d BRP:%d ifdelay:%d\n",
|
|
||||||
can->Baudrate, can->TqSeg1, can->TqSeg2, can->TqProp, can->TqSync, can->BRP, can->innerFrameDelay25us);
|
|
||||||
LOGF(LOG_INFO, "FD Baudrate:%d TqSeg1:%d TqSeg2:%d TqProp:%d TqSync:%d BRP:%d\n",
|
|
||||||
canfd->FDBaudrate, canfd->FDTqSeg1, canfd->FDTqSeg2, canfd->FDTqProp, canfd->FDTqSync, canfd->FDBRP);
|
|
||||||
LOGF(LOG_INFO, "FDMode:0x%x TransceiverMode:0x%x\n", canfd->FDMode, can->transceiver_mode);
|
|
||||||
|
|
||||||
bit_timing.prop_seg = can->TqProp;
|
|
||||||
bit_timing.phase_seg1 = can->TqSeg1;
|
|
||||||
bit_timing.phase_seg2 = can->TqSeg2;
|
|
||||||
bit_timing.brp = can->BRP + 1;
|
|
||||||
bit_timing.sjw = can->TqSync;
|
|
||||||
|
|
||||||
data_bit_timing.prop_seg = canfd->FDTqProp;
|
|
||||||
data_bit_timing.phase_seg1 = canfd->FDTqSeg1;
|
|
||||||
data_bit_timing.phase_seg2 = canfd->FDTqSeg2;
|
|
||||||
data_bit_timing.brp = canfd->FDBRP + 1;
|
|
||||||
data_bit_timing.sjw = canfd->FDTqSync;
|
|
||||||
|
|
||||||
this->termination = termination;
|
|
||||||
|
|
||||||
ctrl_mode = 0;
|
|
||||||
switch (canfd->FDMode) {
|
|
||||||
case NO_CANFD:
|
|
||||||
break;
|
|
||||||
ctrl_mode = 0;
|
|
||||||
case CANFD_ENABLED:
|
|
||||||
case CANFD_BRS_ENABLED:
|
|
||||||
ctrl_mode = CAN_CTRLMODE_FD_NON_ISO;
|
|
||||||
break;
|
|
||||||
case CANFD_ENABLED_ISO:
|
|
||||||
case CANFD_BRS_ENABLED_ISO:
|
|
||||||
ctrl_mode = CAN_CTRLMODE_FD;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (can->transceiver_mode) {
|
|
||||||
case LOOPBACK:
|
|
||||||
ctrl_mode |= CAN_CTRLMODE_LOOPBACK;
|
|
||||||
break;
|
|
||||||
case LISTEN_ONLY:
|
|
||||||
case LISTEN_ALL:
|
|
||||||
ctrl_mode |= CAN_CTRLMODE_LISTENONLY;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
update_bittiming(&bit_timing);
|
|
||||||
if (canfd->FDMode != NO_CANFD) {
|
|
||||||
update_bittiming(&data_bit_timing);
|
|
||||||
} else {
|
|
||||||
data_bit_timing.bitrate = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct can_dev_settings settings = {
|
|
||||||
.device = kernelHandle,
|
|
||||||
.bittiming = bit_timing,
|
|
||||||
.data_bittiming = data_bit_timing,
|
|
||||||
.ctrl_mode = ctrl_mode,
|
|
||||||
.termination = termination,
|
|
||||||
};
|
|
||||||
if(ioctl(driver, SIOCSIFSETTINGS, &settings) < 0) {
|
|
||||||
LOGF(LOG_DEBUG, "device settings ioctl failed %d\n", kernelHandle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBittiming(struct can_bittiming *timing, std::shared_ptr<icsneo::Device> device, icsneo::Network::NetID netid) {
|
|
||||||
if (timing->prop_seg == bit_timing.prop_seg
|
|
||||||
&& timing->phase_seg1 == bit_timing.phase_seg1
|
|
||||||
&& timing->phase_seg2 == bit_timing.phase_seg2
|
|
||||||
&& timing->sjw == bit_timing.sjw
|
|
||||||
&& timing->brp == bit_timing.brp) {
|
|
||||||
LOG(LOG_INFO, "no change in bittiming\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
CAN_SETTINGS *settings = device->settings->getMutableCANSettingsFor(netid);
|
|
||||||
|
|
||||||
settings->SetBaudrate = USE_TQ;
|
|
||||||
settings->TqSeg1 = timing->phase_seg1;
|
|
||||||
settings->TqSeg2 = timing->phase_seg2;
|
|
||||||
settings->TqSync = timing->sjw;
|
|
||||||
settings->TqProp = timing->prop_seg;
|
|
||||||
settings->BRP = timing->brp - 1;
|
|
||||||
|
|
||||||
LOGF(LOG_INFO, "Set Bittiming TqSeg1:%d TqSeg2:%d TqProp:%d TqSync:%d BRP:%d\n",
|
|
||||||
settings->TqSeg1, settings->TqSeg2, settings->TqProp, settings->TqSync, settings->BRP);
|
|
||||||
|
|
||||||
if (! device->settings->apply() ) {
|
|
||||||
LOGF(LOG_ERR, "Unable to set bit timings for %s", name.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
bit_timing = *timing;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setDataBittiming(struct can_bittiming *timing, std::shared_ptr<icsneo::Device> device, icsneo::Network::NetID netid) {
|
|
||||||
if (timing->prop_seg == data_bit_timing.prop_seg
|
|
||||||
&& timing->phase_seg1 == data_bit_timing.phase_seg1
|
|
||||||
&& timing->phase_seg2 == data_bit_timing.phase_seg2
|
|
||||||
&& timing->sjw == data_bit_timing.sjw
|
|
||||||
&& timing->brp == data_bit_timing.brp) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CANFD_SETTINGS *settings = device->settings->getMutableCANFDSettingsFor(netid);
|
|
||||||
|
|
||||||
settings->FDTqSeg1 = timing->phase_seg1;
|
|
||||||
settings->FDTqSeg2 = timing->phase_seg2;
|
|
||||||
settings->FDTqSync = timing->sjw;
|
|
||||||
settings->FDTqProp = timing->prop_seg;
|
|
||||||
settings->FDBRP = timing->brp - 1;
|
|
||||||
|
|
||||||
if (! device->settings->apply() ) {
|
|
||||||
LOGF(LOG_ERR, "Unable to set data bit timings for %s", name.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
data_bit_timing = *timing;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCtrlMode(uint32_t mode, std::shared_ptr<icsneo::Device> device, icsneo::Network::NetID netid) {
|
|
||||||
if (mode == ctrl_mode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((mode & (CAN_CTRLMODE_FD_NON_ISO | CAN_CTRLMODE_FD))
|
|
||||||
!= (ctrl_mode & (CAN_CTRLMODE_FD_NON_ISO | CAN_CTRLMODE_FD))) {
|
|
||||||
CANFD_SETTINGS *settings = device->settings->getMutableCANFDSettingsFor(netid);
|
|
||||||
if (mode & CAN_CTRLMODE_FD_NON_ISO) {
|
|
||||||
if (bit_timing.bitrate == data_bit_timing.bitrate) {
|
|
||||||
settings->FDMode = CANFD_ENABLED;
|
|
||||||
} else {
|
|
||||||
settings->FDMode = CANFD_BRS_ENABLED;
|
|
||||||
}
|
|
||||||
} else if (mode & CAN_CTRLMODE_FD) {
|
|
||||||
if (bit_timing.bitrate == data_bit_timing.bitrate) {
|
|
||||||
settings->FDMode = CANFD_ENABLED_ISO;
|
|
||||||
} else {
|
|
||||||
settings->FDMode = CANFD_BRS_ENABLED_ISO;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
settings->FDMode = NO_CANFD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((mode & (CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_LOOPBACK))
|
|
||||||
!= (ctrl_mode & (CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_LOOPBACK))) {
|
|
||||||
CAN_SETTINGS *settings = device->settings->getMutableCANSettingsFor(netid);
|
|
||||||
if (mode & CAN_CTRLMODE_LISTENONLY) {
|
|
||||||
settings->transceiver_mode = LISTEN_ONLY;
|
|
||||||
} else if (mode & CAN_CTRLMODE_LOOPBACK) {
|
|
||||||
settings->transceiver_mode = LOOPBACK;
|
|
||||||
} else {
|
|
||||||
settings->transceiver_mode = NORMAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! device->settings->apply() ) {
|
|
||||||
LOGF(LOG_ERR, "Unable to set controller mode for %s", name.c_str());
|
|
||||||
}
|
|
||||||
ctrl_mode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTermination(bool termination, std::shared_ptr<icsneo::Device> device, icsneo::Network::NetID netid) {
|
|
||||||
if (termination != this->termination) {
|
|
||||||
if (! device->settings->setTerminationFor(netid, termination) ||
|
|
||||||
! device->settings->apply() ) {
|
|
||||||
LOGF(LOG_ERR, "Unable to set termination for %s", name.c_str());
|
|
||||||
}
|
|
||||||
this->termination = termination;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
icsneo::Network::Type type;
|
icsneo::Network::Type type;
|
||||||
std::string name;
|
std::string name;
|
||||||
int kernelHandle = -1;
|
int kernelHandle = -1;
|
||||||
int ifindex = -1;
|
|
||||||
std::mutex rxBoxLock;
|
std::mutex rxBoxLock;
|
||||||
uint8_t* rxBox = nullptr;
|
uint8_t* rxBox = nullptr;
|
||||||
uint8_t* rxBoxCurrentPosition = nullptr;
|
uint8_t* rxBoxCurrentPosition = nullptr;
|
||||||
size_t rxBoxMessageCount = 0;
|
size_t rxBoxMessageCount = 0;
|
||||||
struct can_bittiming bit_timing;
|
|
||||||
struct can_bittiming data_bit_timing;
|
|
||||||
uint32_t ctrl_mode;
|
|
||||||
bool termination;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class OpenDevice {
|
class OpenDevice {
|
||||||
@@ -530,6 +206,327 @@ std::vector<OpenDevice> openDevices;
|
|||||||
std::vector<std::string /* serial */> failedToOpen;
|
std::vector<std::string /* serial */> failedToOpen;
|
||||||
std::mutex openDevicesMutex;
|
std::mutex openDevicesMutex;
|
||||||
|
|
||||||
|
class RPC {
|
||||||
|
public:
|
||||||
|
RPC() {
|
||||||
|
if(::mkfifo(fifoPath.c_str(), 0777) == -1) {
|
||||||
|
throw std::runtime_error("Error creating RPC FIFO: " + std::string(strerror(errno)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
::chmod(fifoPath.c_str(), 0777);
|
||||||
|
fifo = ::open(fifoPath.c_str(), O_RDWR);
|
||||||
|
if(fifo == -1) {
|
||||||
|
throw std::runtime_error("Error opening RPC FIFO: " + std::string(strerror(errno)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
interrupt = ::eventfd(0, 0);
|
||||||
|
if(interrupt == -1) {
|
||||||
|
throw std::runtime_error("Error opening eventfd: " + std::string(strerror(errno)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
thread = std::thread(&RPC::loop, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~RPC() {
|
||||||
|
uint64_t u = 1;
|
||||||
|
::write(interrupt, &u, sizeof(uint64_t));
|
||||||
|
thread.join();
|
||||||
|
::close(fifo);
|
||||||
|
::close(interrupt);
|
||||||
|
::unlink(fifoPath.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int fifo;
|
||||||
|
int interrupt;
|
||||||
|
std::thread thread;
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
static char buffer[2048];
|
||||||
|
struct pollfd fds[2] = {};
|
||||||
|
fds[0].fd = fifo;
|
||||||
|
fds[0].events = POLLIN;
|
||||||
|
fds[1].fd = interrupt;
|
||||||
|
fds[1].events = POLLIN;
|
||||||
|
while (!stopRunning) {
|
||||||
|
if(::poll(fds, 2, -1) == -1) {
|
||||||
|
LOGF(LOG_WARNING, "Error polling for RPC: %s\n", strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(fds[1].revents & POLLIN) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const auto size = ::read(fifo, buffer, sizeof(buffer));
|
||||||
|
if (size == -1) {
|
||||||
|
LOGF(LOG_WARNING, "Error reading from RPC FIFO: %s\n", strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const auto args = split(std::string(buffer, size));
|
||||||
|
if(args.size() < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& apiVersion = args[0];
|
||||||
|
const auto& command = args[1];
|
||||||
|
if(apiVersion != "1") {
|
||||||
|
LOGF(LOG_WARNING, "Invalid API version, expected '1' got '%s'\n", apiVersion.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(command == "lock_networks") {
|
||||||
|
lockNetworks(args);
|
||||||
|
} else if(command == "unlock_networks") {
|
||||||
|
unlockNetworks(args);
|
||||||
|
} else if(command == "get_network_mutex_status") {
|
||||||
|
getNetworkMutexStatus(args);
|
||||||
|
} else if(command == "get_serials") {
|
||||||
|
getSerials(args);
|
||||||
|
} else {
|
||||||
|
LOGF(LOG_WARNING, "Unknown command '%s'\n", command.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parseNetid(const std::string& arg, icsneo::Network::NetID& netid) {
|
||||||
|
try {
|
||||||
|
netid = static_cast<icsneo::Network::NetID>(std::stoi(arg));
|
||||||
|
return true;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LOGF(LOG_WARNING, "Invalid netid '%s': %s\n", arg.c_str(), e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parseNetids(const std::string& arg, std::set<icsneo::Network::NetID>& netids) {
|
||||||
|
for(auto&& str : split(arg, ',')) {
|
||||||
|
icsneo::Network::NetID nid;
|
||||||
|
if(!parseNetid(str, nid)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
netids.emplace(nid);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::string optionalArg(const std::optional<T>& opt) {
|
||||||
|
return opt ? std::to_string((uint64_t)*opt) : "-1";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool fifoWrite(const std::string& message, const std::string& fifoPath) {
|
||||||
|
int fifo = ::open(fifoPath.c_str(), O_WRONLY);
|
||||||
|
if(fifo == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(::write(fifo, message.c_str(), message.size()) == -1) {
|
||||||
|
::close(fifo);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
::close(fifo);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> split(const std::string& str, char delim = ' ') {
|
||||||
|
if(str.empty())
|
||||||
|
return {};
|
||||||
|
std::vector<std::string> ret;
|
||||||
|
size_t tail = 0;
|
||||||
|
size_t head = 0;
|
||||||
|
while(head < str.size()) {
|
||||||
|
if(str[head] == delim) {
|
||||||
|
ret.emplace_back(&str[tail], head - tail);
|
||||||
|
tail = head + 1;
|
||||||
|
}
|
||||||
|
++head;
|
||||||
|
}
|
||||||
|
ret.emplace_back(&str[tail], head - tail);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string join(const std::vector<std::string>& parts, char delim = ' ') {
|
||||||
|
if(parts.empty())
|
||||||
|
return "";
|
||||||
|
std::string ret = parts[0];
|
||||||
|
for(size_t i = 1; i < parts.size(); i++) {
|
||||||
|
ret += delim + parts[i];
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lockNetworks(const std::vector<std::string>& args) {
|
||||||
|
if(args.size() != 8) {
|
||||||
|
LOGF(LOG_WARNING, "lock_networks requires 8 arguments, got %zu\n", args.size());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& serial = args[2];
|
||||||
|
const auto& clientFifoPath = args[7];
|
||||||
|
|
||||||
|
std::set<icsneo::Network::NetID> netids;
|
||||||
|
if(!parseNetids(args[3], netids)) {
|
||||||
|
fifoWrite("0", clientFifoPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t priority;
|
||||||
|
try {
|
||||||
|
priority = std::stoul(args[4]);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LOGF(LOG_WARNING, "Invalid priority '%s': %s\n", args[4].c_str(), e.what());
|
||||||
|
fifoWrite("0", clientFifoPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ttl;
|
||||||
|
try {
|
||||||
|
ttl = std::stoul(args[5]);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LOGF(LOG_WARNING, "Invalid TTL '%s': %s\n", args[5].c_str(), e.what());
|
||||||
|
fifoWrite("0", clientFifoPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
icsneo::NetworkMutexType type;
|
||||||
|
try {
|
||||||
|
type = static_cast<icsneo::NetworkMutexType>(std::stoi(args[6]));
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LOGF(LOG_WARNING, "Invalid mutex type '%s': %s\n", args[6].c_str(), e.what());
|
||||||
|
fifoWrite("0", clientFifoPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lg(openDevicesMutex);
|
||||||
|
std::shared_ptr<icsneo::Device> device;
|
||||||
|
for(const auto& dev : openDevices) {
|
||||||
|
if(dev.device->getSerial() == serial) {
|
||||||
|
device = dev.device;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!device) {
|
||||||
|
LOGF(LOG_WARNING, "Device with serial '%s' not found\n", serial.c_str());
|
||||||
|
fifoWrite("0", clientFifoPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto locked = device->lockNetworks(netids, priority, ttl, type, [serial](std::shared_ptr<icsneo::Message> msg) -> void {
|
||||||
|
auto mutexMsg = std::dynamic_pointer_cast<icsneo::NetworkMutexMessage>(msg);
|
||||||
|
if(!mutexMsg) {
|
||||||
|
LOG(LOG_WARNING, "Received a message for the network mutex callback which was not a NetworkMutexMessage\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOGF(LOG_INFO, "Received network mutex event for device %s\n", serial.c_str());
|
||||||
|
});
|
||||||
|
if(!locked) {
|
||||||
|
LOGF(LOG_WARNING, "Failed to lock networks for device '%s'\n", icsneo::GetLastError().describe().c_str());
|
||||||
|
fifoWrite("0", clientFifoPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
device->removeMessageCallback(*locked); // client will explicitly poll status
|
||||||
|
fifoWrite("1", clientFifoPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlockNetworks(const std::vector<std::string>& args) {
|
||||||
|
if(args.size() != 5) {
|
||||||
|
LOGF(LOG_WARNING, "unlock_networks requires 5 arguments, got %zu\n", args.size());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& serial = args[2];
|
||||||
|
const auto& clientFifoPath = args[4];
|
||||||
|
|
||||||
|
std::set<icsneo::Network::NetID> netids;
|
||||||
|
if(!parseNetids(args[3], netids)) {
|
||||||
|
fifoWrite("0", clientFifoPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lg(openDevicesMutex);
|
||||||
|
std::shared_ptr<icsneo::Device> device;
|
||||||
|
for(const auto& dev : openDevices) {
|
||||||
|
if(dev.device->getSerial() == serial) {
|
||||||
|
device = dev.device;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!device) {
|
||||||
|
LOGF(LOG_WARNING, "Device with serial '%s' not found\n", serial.c_str());
|
||||||
|
fifoWrite("0", clientFifoPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto success = device->unlockNetworks(netids);
|
||||||
|
if(!success) {
|
||||||
|
LOGF(LOG_WARNING, "Failed to unlock networks for device '%s': %s\n", serial.c_str(), icsneo::GetLastError().describe().c_str());
|
||||||
|
fifoWrite("0", clientFifoPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fifoWrite("1", clientFifoPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getNetworkMutexStatus(const std::vector<std::string>& args) {
|
||||||
|
if(args.size() != 5) {
|
||||||
|
LOGF(LOG_WARNING, "get_network_mutex_status requires 5 arguments, got %zu\n", args.size());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& serial = args[2];
|
||||||
|
const auto& clientFifoPath = args[4];
|
||||||
|
|
||||||
|
icsneo::Network::NetID netid;
|
||||||
|
if(!parseNetid(args[3], netid)) {
|
||||||
|
fifoWrite("0", clientFifoPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lg(openDevicesMutex);
|
||||||
|
std::shared_ptr<icsneo::Device> device;
|
||||||
|
for(const auto& dev : openDevices) {
|
||||||
|
if(dev.device->getSerial() == serial) {
|
||||||
|
device = dev.device;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!device) {
|
||||||
|
LOGF(LOG_WARNING, "Device with serial '%s' not found\n", serial.c_str());
|
||||||
|
fifoWrite("0", clientFifoPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto status = device->getNetworkMutexStatus(netid);
|
||||||
|
|
||||||
|
if(!status) {
|
||||||
|
LOGF(LOG_WARNING, "Failed to get network mutex status for device '%s': %s\n", serial.c_str(), icsneo::GetLastError().describe().c_str());
|
||||||
|
fifoWrite("0", clientFifoPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string id = optionalArg(status->owner_id);
|
||||||
|
const std::string type = optionalArg(status->type);
|
||||||
|
const std::string priority = optionalArg(status->priority);
|
||||||
|
const std::string ttl = optionalArg(status->ttlMs);
|
||||||
|
std::vector<std::string> networkStrs;
|
||||||
|
for(const auto& net : status->networks) {
|
||||||
|
networkStrs.push_back(std::to_string((neonetid_t)net));
|
||||||
|
}
|
||||||
|
const std::string networks = join(networkStrs, ',');
|
||||||
|
const std::string event = optionalArg(status->event);
|
||||||
|
|
||||||
|
const std::string response = join({"1", id, type, priority, ttl, networks, event});
|
||||||
|
|
||||||
|
fifoWrite(response, clientFifoPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getSerials(const std::vector<std::string>& args) {
|
||||||
|
if(args.size() != 3) {
|
||||||
|
LOGF(LOG_WARNING, "get_serials requires 3 arguments, got %zu\n", args.size());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& clientFifoPath = args[2];
|
||||||
|
std::string response = "1";
|
||||||
|
std::lock_guard<std::mutex> lg(openDevicesMutex);
|
||||||
|
for(const auto& dev : openDevices) {
|
||||||
|
response += ' ' + dev.device->getSerial();
|
||||||
|
}
|
||||||
|
fifoWrite(response, clientFifoPath);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
std::string& replaceInPlace(std::string& str, char o, const std::string& n) {
|
std::string& replaceInPlace(std::string& str, char o, const std::string& n) {
|
||||||
size_t start_pos = 0;
|
size_t start_pos = 0;
|
||||||
const size_t new_len = n.length();
|
const size_t new_len = n.length();
|
||||||
@@ -574,6 +571,7 @@ void usage(std::string executableName) {
|
|||||||
std::cerr << "\t --devices\t\t\tList supported devices\n";
|
std::cerr << "\t --devices\t\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>\t\tOnly connect to devices with serial\n\t\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";
|
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";
|
||||||
|
std::cerr << "\t --fifo-path <path>\t\tPath to RPC FIFO for libicsneo control\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void terminateSignal(int signal) {
|
void terminateSignal(int signal) {
|
||||||
@@ -581,8 +579,8 @@ void terminateSignal(int signal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void searchForDevices() {
|
void searchForDevices() {
|
||||||
auto found = icsneo::FindAllDevices();
|
|
||||||
std::lock_guard<std::mutex> lg(openDevicesMutex);
|
std::lock_guard<std::mutex> lg(openDevicesMutex);
|
||||||
|
auto found = icsneo::FindAllDevices();
|
||||||
|
|
||||||
// Open devices we have not seen before
|
// Open devices we have not seen before
|
||||||
for(auto& dev : found) {
|
for(auto& dev : found) {
|
||||||
@@ -637,7 +635,7 @@ void searchForDevices() {
|
|||||||
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, net.getType(), newDevice.device->getType());
|
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;
|
||||||
@@ -652,50 +650,37 @@ void searchForDevices() {
|
|||||||
LOGF(LOG_INFO, "%s failed to create network interfaces. Will keep trying...\n", newDevice.device->describe().c_str());
|
LOGF(LOG_INFO, "%s failed to create network interfaces. Will keep trying...\n", newDevice.device->describe().c_str());
|
||||||
failedToOpen.push_back(serial);
|
failedToOpen.push_back(serial);
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (driverMinor > 0) {
|
if (driverMinor > 0) {
|
||||||
for(const auto& net : supportedNetworks) {
|
for(const auto& net : supportedNetworks) {
|
||||||
if (net.getType() != icsneo::Network::Type::CAN)
|
if (net.getType() != icsneo::Network::Type::CAN)
|
||||||
continue;
|
continue;
|
||||||
const CAN_SETTINGS *can = newDevice.device->settings->getCANSettingsFor(net.getNetID());
|
newDevice.interfaces[net.getNetID()]->reportBaudrates(
|
||||||
const CANFD_SETTINGS *fd = newDevice.device->settings->getCANFDSettingsFor(net.getNetID());
|
newDevice.device->settings->getBaudrateFor(net.getNetID()),
|
||||||
bool termination = newDevice.device->settings->isTerminationEnabledFor(net.getNetID())
|
newDevice.device->settings->getFDBaudrateFor(net.getNetID())
|
||||||
.value_or(false);
|
);
|
||||||
newDevice.interfaces[net.getNetID()]->storeCanSettings(can, fd, termination);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create rx listener
|
// Create rx listener
|
||||||
newDevice.device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([serial](std::shared_ptr<icsneo::Message> message) {
|
for(auto&& interface : newDevice.interfaces) {
|
||||||
|
newDevice.device->addMessageCallback(std::make_shared<icsneo::MessageCallback>([interface = interface.second](std::shared_ptr<icsneo::Message> message) {
|
||||||
const auto frame = std::static_pointer_cast<icsneo::Frame>(message);
|
const auto frame = std::static_pointer_cast<icsneo::Frame>(message);
|
||||||
const auto messageType = frame->network.getType();
|
const auto messageType = frame->network.getType();
|
||||||
const OpenDevice* openDevice = nullptr;
|
|
||||||
std::lock_guard<std::mutex> lg(openDevicesMutex);
|
|
||||||
for(const auto& dev : openDevices) {
|
|
||||||
if(dev.device->getSerial() == serial) {
|
|
||||||
openDevice = &dev;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(frame->type == icsneo::Message::Type::CANErrorCount) {
|
|
||||||
const auto errmsg = std::static_pointer_cast<icsneo::CANErrorCountMessage>(message);
|
|
||||||
openDevice->interfaces.at(frame->network.getNetID())->reportErrorCount(errmsg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(frame->type != icsneo::Message::Type::Frame) {
|
if(frame->type != icsneo::Message::Type::Frame) {
|
||||||
LOG(LOG_ERR, "Dropping message: received invalid message type, expected RawMessage\n");
|
LOG(LOG_ERR, "Dropping message: received invalid message type, expected RawMessage\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(messageType == icsneo::Network::Type::CAN) {
|
if(messageType == icsneo::Network::Type::CAN) {
|
||||||
openDevice->interfaces.at(frame->network.getNetID())->addReceivedMessageToQueue<neomessage_can_t>(frame);
|
interface->addReceivedMessageToQueue<neomessage_can_t>(frame);
|
||||||
} else if(messageType == icsneo::Network::Type::Ethernet) {
|
} else if(messageType == icsneo::Network::Type::Ethernet) {
|
||||||
openDevice->interfaces.at(frame->network.getNetID())->addReceivedMessageToQueue<neomessage_eth_t>(frame);
|
interface->addReceivedMessageToQueue<neomessage_eth_t>(frame);
|
||||||
} else
|
} else {
|
||||||
LOG(LOG_ERR, "Dropping message, only CAN and Ethernet are currently supported\n");
|
LOG(LOG_ERR, "Dropping message, only CAN and Ethernet are currently supported\n");
|
||||||
}));
|
}
|
||||||
|
}, std::make_shared<icsneo::MessageFilter>(interface.first)));
|
||||||
|
}
|
||||||
|
|
||||||
LOGF(LOG_INFO, "%s connected\n", newDevice.device->describe().c_str());
|
LOGF(LOG_INFO, "%s connected\n", newDevice.device->describe().c_str());
|
||||||
failedToOpen.erase(std::remove_if(failedToOpen.begin(), failedToOpen.end(), [&serial](const std::string& s) -> bool {
|
failedToOpen.erase(std::remove_if(failedToOpen.begin(), failedToOpen.end(), [&serial](const std::string& s) -> bool {
|
||||||
@@ -785,6 +770,8 @@ int main(int argc, char** argv) {
|
|||||||
std::cerr << "Invalid input for scan-interval-ms\n";
|
std::cerr << "Invalid input for scan-interval-ms\n";
|
||||||
return EX_USAGE;
|
return EX_USAGE;
|
||||||
}
|
}
|
||||||
|
} else if(arg == "--fifo-path" && i + 1 <= argc) {
|
||||||
|
fifoPath = argv[++i];
|
||||||
} else {
|
} else {
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
return EX_USAGE;
|
return EX_USAGE;
|
||||||
@@ -854,12 +841,6 @@ int main(int argc, char** argv) {
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int netlink_socket = open_netlink_socket();
|
|
||||||
if (netlink_socket < 0) {
|
|
||||||
LOGF(LOG_ERR, "Unable to open netlink socket\nError %d: %s\n", errno, strerror(errno));
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Daemonize if necessary
|
// Daemonize if necessary
|
||||||
if(runningAsDaemon) {
|
if(runningAsDaemon) {
|
||||||
LOG(LOG_INFO, "The daemon will now continue to run in the background\n");
|
LOG(LOG_INFO, "The daemon will now continue to run in the background\n");
|
||||||
@@ -873,55 +854,33 @@ int main(int argc, char** argv) {
|
|||||||
LOG(LOG_INFO, "Waiting for connections...\n");
|
LOG(LOG_INFO, "Waiting for connections...\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<RPC> rpc;
|
||||||
|
if(!fifoPath.empty()) {
|
||||||
|
try {
|
||||||
|
rpc = std::make_unique<RPC>();
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LOGF(LOG_ERR, "Failed to set up RPC: %s\n", e.what());
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::thread searchThread(deviceSearchThread);
|
std::thread searchThread(deviceSearchThread);
|
||||||
|
|
||||||
while(!stopRunning) {
|
while(!stopRunning) {
|
||||||
fd_set fds;
|
fd_set fds;
|
||||||
FD_ZERO(&fds);
|
FD_ZERO(&fds);
|
||||||
FD_SET(driver, &fds);
|
FD_SET(driver, &fds);
|
||||||
FD_SET(netlink_socket, &fds);
|
|
||||||
|
|
||||||
int max_fd = (driver > netlink_socket)?driver:netlink_socket;
|
|
||||||
|
|
||||||
struct timeval timeout = {};
|
struct timeval timeout = {};
|
||||||
timeout.tv_sec = 1;
|
timeout.tv_sec = 1;
|
||||||
|
|
||||||
auto ret = select(max_fd + 1, &fds, NULL, NULL, &timeout);
|
auto ret = select(driver + 1, &fds, NULL, NULL, &timeout);
|
||||||
if(ret == -1) {
|
if(ret == -1) {
|
||||||
// Fatal error
|
// Fatal error
|
||||||
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;
|
||||||
}
|
} else if(ret != 0) {
|
||||||
if (FD_ISSET(netlink_socket, &fds)) {
|
|
||||||
// Kernel sent some information via netlink, handle it.
|
|
||||||
read_netlink_msgs(netlink_socket, [](int ifindex, int type, void *data) {
|
|
||||||
for(auto& dev : openDevices) {
|
|
||||||
for(auto& netifPair : dev.interfaces) {
|
|
||||||
auto netid = netifPair.first;
|
|
||||||
auto iface = netifPair.second;
|
|
||||||
if (iface->getIfIndex() != ifindex) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
switch (type) {
|
|
||||||
case IFLA_CAN_BITTIMING:
|
|
||||||
iface->setBittiming((struct can_bittiming *) data, dev.device, netid);
|
|
||||||
break;
|
|
||||||
case IFLA_CAN_DATA_BITTIMING:
|
|
||||||
iface->setDataBittiming((struct can_bittiming *) data, dev.device, netid);
|
|
||||||
break;
|
|
||||||
case IFLA_CAN_TERMINATION:
|
|
||||||
iface->setTermination(*((uint16_t *) data) != 0, dev.device, netid);
|
|
||||||
break;
|
|
||||||
case IFLA_CAN_CTRLMODE:
|
|
||||||
iface->setCtrlMode(((struct can_ctrlmode *) data)->flags, dev.device, netid);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (FD_ISSET(driver, &fds)) {
|
|
||||||
// Kernel says there are some new transmit messages waiting to go out.
|
// Kernel says there are some new transmit messages waiting to go out.
|
||||||
// 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;
|
||||||
@@ -955,9 +914,6 @@ int main(int argc, char** argv) {
|
|||||||
} else if (! dev.device->settings->setFDBaudrateFor(netid, info.bytes)) {
|
} else if (! dev.device->settings->setFDBaudrateFor(netid, info.bytes)) {
|
||||||
LOGF(LOG_ERR, "Unable to set fd baudrate for device %s\n",
|
LOGF(LOG_ERR, "Unable to set fd baudrate for device %s\n",
|
||||||
netifPair.second->getName().c_str());
|
netifPair.second->getName().c_str());
|
||||||
} else if (! dev.device->settings->setTerminationFor(netid, false)) {
|
|
||||||
LOGF(LOG_ERR, "Unable to set termination for device %s\n",
|
|
||||||
netifPair.second->getName().c_str());
|
|
||||||
} else if (! dev.device->settings->apply()) {
|
} else if (! dev.device->settings->apply()) {
|
||||||
LOGF(LOG_ERR, "Unable to apply settings for device %s\n",
|
LOGF(LOG_ERR, "Unable to apply settings for device %s\n",
|
||||||
netifPair.second->getName().c_str());
|
netifPair.second->getName().c_str());
|
||||||
@@ -978,25 +934,35 @@ int main(int argc, char** argv) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sent = false;
|
const auto transmit = [&] {
|
||||||
std::lock_guard<std::mutex> lg(openDevicesMutex);
|
std::lock_guard<std::mutex> lg(openDevicesMutex);
|
||||||
for(auto& dev : openDevices) {
|
for(auto it = openDevices.begin(); it != openDevices.end(); ++it) {
|
||||||
|
auto& dev = *it;
|
||||||
for(auto& netifPair : dev.interfaces) {
|
for(auto& netifPair : dev.interfaces) {
|
||||||
if(netifPair.second->getKernelHandle() != msg->netid)
|
if(netifPair.second->getKernelHandle() != msg->netid)
|
||||||
continue;
|
continue;
|
||||||
|
if(!dev.device->isOpen() || !dev.device->isOnline() || dev.device->isDisconnected()) {
|
||||||
|
LOGF(LOG_ERR, "Message dropped, %s is not open and online\n", dev.device->getSerial().c_str());
|
||||||
|
openDevices.erase(it);
|
||||||
|
return;
|
||||||
|
}
|
||||||
msg->netid = static_cast<uint16_t>(netifPair.first);
|
msg->netid = static_cast<uint16_t>(netifPair.first);
|
||||||
auto txMsg = icsneo::CreateMessageFromNeoMessage(reinterpret_cast<neomessage_t*>(msg));
|
auto txMsg = icsneo::CreateMessageFromNeoMessage(reinterpret_cast<neomessage_t*>(msg));
|
||||||
auto tx = std::dynamic_pointer_cast<icsneo::Frame>(txMsg);
|
auto tx = std::dynamic_pointer_cast<icsneo::Frame>(txMsg);
|
||||||
if(!tx || !dev.device->transmit(tx))
|
if(!tx) {
|
||||||
break;
|
LOG(LOG_ERR, "Message dropped, invalid transmit message\n");
|
||||||
sent = true;
|
return;
|
||||||
break;
|
}
|
||||||
|
if(!dev.device->transmit(tx)) {
|
||||||
|
LOGF(LOG_ERR, "Message dropped, unable to transmit: %s\n", icsneo::GetLastError().describe().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if(sent)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if(!sent)
|
|
||||||
LOG(LOG_ERR, "Message dropped, could not find the device the kernel referenced\n");
|
LOG(LOG_ERR, "Message dropped, could not find the device the kernel referenced\n");
|
||||||
|
};
|
||||||
|
transmit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <sys/uio.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <linux/if_link.h>
|
|
||||||
#include <linux/if_arp.h>
|
|
||||||
#include <linux/netlink.h>
|
|
||||||
#include <linux/can/netlink.h>
|
|
||||||
#include <linux/rtnetlink.h>
|
|
||||||
|
|
||||||
#include "netlink.h"
|
|
||||||
|
|
||||||
bool read_netlink_msgs(int s, msg_callback callback)
|
|
||||||
{
|
|
||||||
struct nlmsghdr buf[8192/sizeof(struct nlmsghdr)];
|
|
||||||
|
|
||||||
struct nlmsghdr *nh;
|
|
||||||
int len = recv(s, buf, sizeof(buf), 0);
|
|
||||||
|
|
||||||
for (nh = (struct nlmsghdr *) buf; NLMSG_OK (nh, len);
|
|
||||||
nh = NLMSG_NEXT (nh, len)) {
|
|
||||||
/* The end of multipart message */
|
|
||||||
if (nh->nlmsg_type == NLMSG_DONE) {
|
|
||||||
printf("Done\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nh->nlmsg_type == NLMSG_ERROR) {
|
|
||||||
printf("Error\n");
|
|
||||||
} else if (nh->nlmsg_type == RTM_NEWLINK) {
|
|
||||||
struct ifinfomsg *iface = NLMSG_DATA(nh);
|
|
||||||
int attrlen = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*iface));
|
|
||||||
if (iface->ifi_type == ARPHRD_CAN) {
|
|
||||||
for (struct rtattr *rta = IFLA_RTA(iface); RTA_OK(rta, attrlen); rta = RTA_NEXT(rta, attrlen)) {
|
|
||||||
switch(rta->rta_type) {
|
|
||||||
case IFLA_LINKINFO:
|
|
||||||
{
|
|
||||||
int attr2len = RTA_PAYLOAD(rta);
|
|
||||||
char *kind = NULL;
|
|
||||||
void *data = NULL;
|
|
||||||
int data_len;
|
|
||||||
for (struct rtattr *rta2 = RTA_DATA(rta); RTA_OK(rta2, attr2len);
|
|
||||||
rta2 = RTA_NEXT(rta2, attr2len)) {
|
|
||||||
switch (rta2->rta_type) {
|
|
||||||
case IFLA_INFO_KIND:
|
|
||||||
kind = RTA_DATA(rta2);
|
|
||||||
break;
|
|
||||||
case IFLA_INFO_DATA:
|
|
||||||
data = RTA_DATA(rta2);
|
|
||||||
data_len = RTA_PAYLOAD(rta2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (kind && strcmp(kind, "can") == 0 && data) {
|
|
||||||
attr2len = data_len;
|
|
||||||
for (struct rtattr *rta2 = data; RTA_OK(rta2, attr2len);
|
|
||||||
rta2 = RTA_NEXT(rta2, attr2len)) {
|
|
||||||
if (callback) {
|
|
||||||
callback(iface->ifi_index, rta2->rta_type, RTA_DATA(rta2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int open_netlink_socket()
|
|
||||||
{
|
|
||||||
int s = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE);
|
|
||||||
|
|
||||||
if (s < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sockaddr_nl addr = {
|
|
||||||
.nl_family = AF_NETLINK,
|
|
||||||
.nl_groups = RTMGRP_LINK,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int group = RTMGRP_LINK;
|
|
||||||
if (setsockopt(s, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group))) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef void (*msg_callback)(int /* ifindex */, int /* rta_type */, void * /* data */);
|
|
||||||
|
|
||||||
bool read_netlink_msgs(int s, msg_callback callback);
|
|
||||||
|
|
||||||
int open_netlink_socket();
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
Vendored
+1
-1
Submodule third-party/libicsneo updated: 7eeb1b6c38...3aaacb6cc8
Reference in New Issue
Block a user