AgIsoStack++
A control-function-focused implementation of the major ISOBUS and J1939 protocols
Loading...
Searching...
No Matches
socket_can_interface.cpp
Go to the documentation of this file.
1//================================================================================================
8//================================================================================================
11#include "isobus/utility/system_timing.hpp"
12
13#include <linux/can.h>
14#include <linux/can/raw.h>
15#include <net/if.h>
16#include <poll.h>
17#include <sys/ioctl.h>
18#include <sys/time.h>
19#include <unistd.h>
20#include <algorithm>
21#include <chrono>
22#include <cstdint>
23#include <cstring>
24#include <limits>
25
26namespace isobus
27{
28 SocketCANInterface::SocketCANInterface(const std::string deviceName) :
29 pCANDevice(new sockaddr_can),
30 name(deviceName),
31 fileDescriptor(-1)
32 {
33 if (nullptr != pCANDevice)
34 {
35 std::memset(pCANDevice, 0, sizeof(struct sockaddr_can));
36 }
37 }
38
40 {
41 close();
42
43 if (nullptr != pCANDevice)
44 {
45 delete pCANDevice;
46 pCANDevice = nullptr;
47 }
48 }
49
51 {
52 return (-1 != fileDescriptor);
53 }
54
56 {
57 return name;
58 }
59
65
67 {
68 fileDescriptor = socket(PF_CAN, SOCK_RAW, CAN_RAW);
69
70 if (fileDescriptor >= 0)
71 {
72 struct ifreq interfaceRequestStructure;
73 const int RECEIVE_OWN_MESSAGES = 0;
74 const int DROP_MONITOR = 1;
75 const int TIMESTAMPING = 0x58;
76 const int TIMESTAMP = 1;
77 memset(&interfaceRequestStructure, 0, sizeof(interfaceRequestStructure));
78 strncpy(interfaceRequestStructure.ifr_name, name.c_str(), sizeof(interfaceRequestStructure.ifr_name));
79 setsockopt(fileDescriptor, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &RECEIVE_OWN_MESSAGES, sizeof(RECEIVE_OWN_MESSAGES));
80 setsockopt(fileDescriptor, SOL_SOCKET, SO_RXQ_OVFL, &DROP_MONITOR, sizeof(DROP_MONITOR));
81
82 if (setsockopt(fileDescriptor, SOL_SOCKET, SO_TIMESTAMPING, &TIMESTAMPING, sizeof(TIMESTAMPING)) < 0)
83 {
84 setsockopt(fileDescriptor, SOL_SOCKET, SO_TIMESTAMP, &TIMESTAMP, sizeof(TIMESTAMP));
85 }
86
87 if (ioctl(fileDescriptor, SIOCGIFINDEX, &interfaceRequestStructure) >= 0)
88 {
89 memset(pCANDevice, 0, sizeof(sockaddr_can));
90 pCANDevice->can_family = AF_CAN;
91 pCANDevice->can_ifindex = interfaceRequestStructure.ifr_ifindex;
92
93 if (bind(fileDescriptor, (struct sockaddr *)pCANDevice, sizeof(struct sockaddr)) < 0)
94 {
95 close();
96 }
97 }
98 else
99 {
100 close();
101 }
102 }
103 else
104 {
105 close();
106 }
107 }
108
110 {
111 struct pollfd pollingFileDescriptor;
112 bool retVal = false;
113
114 pollingFileDescriptor.fd = fileDescriptor;
115 pollingFileDescriptor.events = POLLIN;
116 pollingFileDescriptor.revents = 0;
117
118 if (1 == poll(&pollingFileDescriptor, 1, 100))
119 {
120 canFrame.timestamp_us = std::numeric_limits<std::uint64_t>::max();
121 struct can_frame txFrame;
122 struct msghdr message;
123 struct iovec segment;
124
125 char lControlMessage[CMSG_SPACE(sizeof(struct timeval) + (3 * sizeof(struct timespec)) + sizeof(std::uint32_t))];
126
127 segment.iov_base = &txFrame;
128 segment.iov_len = sizeof(struct can_frame);
129 message.msg_iov = &segment;
130 message.msg_iovlen = 1;
131 message.msg_control = &lControlMessage;
132 message.msg_controllen = sizeof(lControlMessage);
133 message.msg_name = pCANDevice;
134 message.msg_namelen = sizeof(struct sockaddr_can);
135 message.msg_flags = 0;
136
137 if (recvmsg(fileDescriptor, &message, 0) > 0)
138 {
139 if (0 == (txFrame.can_id & CAN_ERR_FLAG))
140 {
141 if (0 != (txFrame.can_id & CAN_EFF_FLAG))
142 {
143 canFrame.identifier = (txFrame.can_id & CAN_EFF_MASK);
144 canFrame.isExtendedFrame = true;
145 }
146 else
147 {
148 canFrame.identifier = (txFrame.can_id & CAN_SFF_MASK);
149 canFrame.isExtendedFrame = false;
150 }
151 canFrame.dataLength = txFrame.can_dlc;
152 memset(canFrame.data, 0, sizeof(canFrame.data));
153 memcpy(canFrame.data, txFrame.data, canFrame.dataLength);
154
155 for (struct cmsghdr *pControlMessage = CMSG_FIRSTHDR(&message); (nullptr != pControlMessage) && (SOL_SOCKET == pControlMessage->cmsg_level); pControlMessage = CMSG_NXTHDR(&message, pControlMessage))
156 {
157 switch (pControlMessage->cmsg_type)
158 {
159 case SO_TIMESTAMP:
160 {
161 struct timeval *time = (struct timeval *)CMSG_DATA(pControlMessage);
162
163 if (std::numeric_limits<std::uint64_t>::max() == canFrame.timestamp_us)
164 {
165 canFrame.timestamp_us = static_cast<std::uint64_t>(time->tv_usec) + (static_cast<std::uint64_t>(time->tv_sec) * 1000000);
166 }
167 }
168 break;
169
170 case SO_TIMESTAMPING:
171 {
172 struct timespec *time = (struct timespec *)(CMSG_DATA(pControlMessage));
173 canFrame.timestamp_us = (static_cast<std::uint64_t>(time[2].tv_nsec) / 1000) + (static_cast<std::uint64_t>(time[2].tv_sec) * 1000000);
174 }
175 break;
176 }
177 }
178 retVal = true;
179 }
180 }
181 else if (errno == ENETDOWN)
182 {
183 LOG_CRITICAL("[SocketCAN] " + get_device_name() + " interface is down.");
184 close();
185 }
186 }
187 else if (pollingFileDescriptor.revents & (POLLERR | POLLHUP))
188 {
189 close();
190 }
191 return retVal;
192 }
193
195 {
196 struct can_frame txFrame;
197 bool retVal = false;
198
199 txFrame.can_id = canFrame.identifier;
200 txFrame.can_dlc = canFrame.dataLength;
201 memcpy(txFrame.data, canFrame.data, canFrame.dataLength);
202
203 if (canFrame.isExtendedFrame)
204 {
205 txFrame.can_id |= CAN_EFF_FLAG;
206 }
207
208 if (write(fileDescriptor, &txFrame, sizeof(struct can_frame)) > 0)
209 {
210 retVal = true;
211 }
212 else if (errno == ENETDOWN)
213 {
214 LOG_CRITICAL("[SocketCAN] " + get_device_name() + " interface is down.");
215 close();
216 }
217 return retVal;
218 }
219
220 bool SocketCANInterface::set_name(const std::string &newName)
221 {
222 bool retVal = false;
223
224 if (!get_is_valid())
225 {
226 name = newName;
227 retVal = true;
228 }
229 return retVal;
230 }
231}
A class that acts as a logging sink. The intent is that someone could make their own derived class of...
A CAN frame for interfacing with a hardware layer, like socket CAN or other interface.
std::uint8_t dataLength
The length of the data used in the frame.
std::uint32_t identifier
The 32 bit identifier of the frame.
std::uint64_t timestamp_us
A microsecond timestamp.
bool isExtendedFrame
Denotes if the frame is extended format.
std::uint8_t data[8]
The data payload of the frame.
std::string name
The device name.
int fileDescriptor
File descriptor for the socket.
bool write_frame(const isobus::CANMessageFrame &canFrame) override
Writes a frame to the bus (synchronous)
SocketCANInterface(const std::string deviceName)
Constructor for the socket CAN driver.
std::string get_device_name() const
Returns the device name the driver is using.
bool read_frame(isobus::CANMessageFrame &canFrame) override
Returns a frame from the hardware (synchronous), or false if no frame can be read.
void close() override
Closes the socket.
bool get_is_valid() const override
Returns if the socket connection is valid.
void open() override
Connects to the socket.
virtual ~SocketCANInterface()
The destructor for SocketCANInterface.
struct sockaddr_can * pCANDevice
The structure for CAN sockets.
bool set_name(const std::string &newName)
Changes the name of the device to use, which only works if the device is not open.
This namespace encompasses all of the ISO11783 stack's functionality to reduce global namespace pollu...
An interface for using socket CAN on linux. Mostly for testing, but it could be used in any applicati...