Line data Source code
1 : /********************************************************************************
2 : * Copyright (c) 2024 Accenture
3 : *
4 : * This program and the accompanying materials are made available under the
5 : * terms of the Apache License Version 2.0 which is available at
6 : * https://www.apache.org/licenses/LICENSE-2.0
7 : *
8 : * SPDX-License-Identifier: Apache-2.0
9 : ********************************************************************************/
10 :
11 : #include "can/SocketCanTransceiver.h"
12 :
13 : #include <can/CanLogger.h>
14 : #include <can/canframes/ICANFrameSentListener.h>
15 : #include <linux/can.h>
16 : #include <linux/can/raw.h>
17 : #include <net/if.h>
18 : #include <sys/ioctl.h>
19 : #include <sys/socket.h>
20 :
21 : #include <fcntl.h>
22 : #include <signal.h>
23 : #include <type_traits>
24 : #include <unistd.h>
25 :
26 : #include <etl/char_traits.h>
27 : #include <etl/error_handler.h>
28 : #include <etl/span.h>
29 : #include <sys/types.h>
30 :
31 : static_assert(
32 : std::is_standard_layout<::can::CANFrame>::value
33 : && std::is_trivially_destructible<::can::CANFrame>::value,
34 : "check for UB while passing through TxQueue");
35 :
36 : namespace can
37 : {
38 :
39 : using ::util::logger::CAN;
40 : using ::util::logger::Logger;
41 :
42 : namespace
43 : {
44 :
45 : template<typename F>
46 0 : void signalGuarded(F&& function)
47 : {
48 : sigset_t set, oldSet;
49 0 : sigfillset(&set);
50 0 : pthread_sigmask(SIG_SETMASK, &set, &oldSet);
51 0 : ::std::forward<F>(function)();
52 0 : pthread_sigmask(SIG_SETMASK, &oldSet, nullptr);
53 0 : }
54 :
55 : } // namespace
56 :
57 : // needed if ODR-used
58 : size_t const SocketCanTransceiver::TX_QUEUE_SIZE_BYTES;
59 :
60 1 : SocketCanTransceiver::SocketCanTransceiver(DeviceConfig const& config)
61 1 : : AbstractCANTransceiver(config.busId)
62 1 : , _txQueue()
63 1 : , _txReader(_txQueue)
64 1 : , _txWriter(_txQueue)
65 1 : , _config(config)
66 1 : , _fileDescriptor(-1)
67 2 : , _writable(false)
68 1 : {}
69 :
70 0 : ICanTransceiver::ErrorCode SocketCanTransceiver::init()
71 : {
72 0 : if (!isInState(State::CLOSED))
73 : {
74 0 : return ErrorCode::CAN_ERR_ILLEGAL_STATE;
75 : }
76 0 : setState(State::INITIALIZED);
77 0 : return ErrorCode::CAN_ERR_OK;
78 : }
79 :
80 0 : ICanTransceiver::ErrorCode SocketCanTransceiver::open()
81 : {
82 0 : if (!isInState(State::INITIALIZED))
83 : {
84 0 : return ErrorCode::CAN_ERR_ILLEGAL_STATE;
85 : }
86 0 : signalGuarded([this] { guardedOpen(); });
87 0 : setState(State::OPEN);
88 0 : _writable.store(true);
89 0 : return ErrorCode::CAN_ERR_OK;
90 : }
91 :
92 0 : ICanTransceiver::ErrorCode SocketCanTransceiver::open(CANFrame const& /* frame */)
93 : {
94 0 : ETL_ASSERT_FAIL(ETL_ERROR_GENERIC("not implemented"));
95 : return ErrorCode::CAN_ERR_ILLEGAL_STATE;
96 : }
97 :
98 0 : ICanTransceiver::ErrorCode SocketCanTransceiver::close()
99 : {
100 0 : if (!isInState(State::OPEN) && !isInState(State::MUTED))
101 : {
102 0 : return ErrorCode::CAN_ERR_ILLEGAL_STATE;
103 : }
104 0 : signalGuarded([this] { guardedClose(); });
105 0 : _writable.store(false);
106 0 : setState(State::CLOSED);
107 0 : return ErrorCode::CAN_ERR_OK;
108 : }
109 :
110 0 : void SocketCanTransceiver::shutdown() {}
111 :
112 0 : ICanTransceiver::ErrorCode SocketCanTransceiver::write(CANFrame const& frame)
113 : {
114 0 : return writeImpl(frame, nullptr);
115 : }
116 :
117 : ICanTransceiver::ErrorCode
118 0 : SocketCanTransceiver::write(CANFrame const& frame, ICANFrameSentListener& listener)
119 : {
120 0 : return writeImpl(frame, &listener);
121 : }
122 :
123 : ICanTransceiver::ErrorCode
124 0 : SocketCanTransceiver::writeImpl(CANFrame const& frame, ICANFrameSentListener* listener)
125 : {
126 0 : if (!_writable.load(std::memory_order_relaxed))
127 : {
128 0 : return ErrorCode::CAN_ERR_ILLEGAL_STATE;
129 : }
130 0 : auto memory = _txWriter.allocate(TX_ELEMENT_SIZE_BYTES);
131 0 : if (memory.size() < TX_ELEMENT_SIZE_BYTES)
132 : {
133 0 : return ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL;
134 : }
135 0 : ::std::memcpy(memory.data(), &frame, sizeof(frame));
136 0 : ::std::memcpy(memory.data() + sizeof(frame), static_cast<void*>(&listener), sizeof(void*));
137 0 : _txWriter.commit();
138 0 : return ErrorCode::CAN_ERR_OK;
139 : }
140 :
141 0 : ICanTransceiver::ErrorCode SocketCanTransceiver::mute()
142 : {
143 0 : if (!isInState(State::OPEN))
144 : {
145 0 : return ErrorCode::CAN_ERR_ILLEGAL_STATE;
146 : }
147 0 : _writable.store(false);
148 0 : setState(State::MUTED);
149 0 : return ErrorCode::CAN_ERR_OK;
150 : }
151 :
152 0 : ICanTransceiver::ErrorCode SocketCanTransceiver::unmute()
153 : {
154 0 : if (!isInState(State::MUTED))
155 : {
156 0 : return ErrorCode::CAN_ERR_ILLEGAL_STATE;
157 : }
158 0 : setState(State::OPEN);
159 0 : _writable.store(true);
160 0 : return ErrorCode::CAN_ERR_OK;
161 : }
162 :
163 0 : uint32_t SocketCanTransceiver::getBaudrate() const { return 500000U; }
164 :
165 0 : uint16_t SocketCanTransceiver::getHwQueueTimeout() const { return 1U; }
166 :
167 0 : void SocketCanTransceiver::run(int maxSentPerRun, int maxReceivedPerRun)
168 : {
169 0 : signalGuarded([this, maxSentPerRun, maxReceivedPerRun]
170 0 : { guardedRun(maxSentPerRun, maxReceivedPerRun); });
171 0 : }
172 :
173 0 : void SocketCanTransceiver::guardedOpen()
174 : {
175 : // NOLINTBEGIN(cppcoreguidelines-pro-type-vararg): Logger API is variadic by design.
176 0 : char const* const name = _config.name;
177 0 : int error = 0;
178 0 : int const fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
179 0 : if (fd < 0)
180 : {
181 0 : Logger::error(
182 : CAN, "[SocketCanTransceiver] Failed to create socket (node=%s, error=%d)", name, fd);
183 0 : return;
184 : }
185 :
186 : struct ifreq ifr;
187 0 : etl::strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name) / sizeof(ifr.ifr_name[0]));
188 0 : error = ioctl(fd, SIOCGIFINDEX, &ifr);
189 0 : if (error < 0)
190 : {
191 0 : Logger::error(
192 : CAN, "[SocketCanTransceiver] Failed to ioctl socket (node=%s, error=%d)", name, error);
193 0 : return;
194 : }
195 :
196 0 : int const enable_canfd = 1;
197 0 : error = setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &enable_canfd, sizeof(enable_canfd));
198 0 : if (error < 0)
199 : {
200 0 : Logger::error(
201 : CAN,
202 : "[SocketCanTransceiver] Failed to setsockopt socket (node=%s, error=%d)",
203 : name,
204 : error);
205 0 : return;
206 : }
207 :
208 0 : error = fcntl(fd, F_SETFL, O_NONBLOCK);
209 0 : if (error < 0)
210 : {
211 0 : Logger::error(
212 : CAN,
213 : "[SocketCanTransceiver] Failed to switch to non-blocking mode (node=%s, error=%d)",
214 : name,
215 : error);
216 0 : return;
217 : }
218 :
219 : struct sockaddr_can addr;
220 0 : ::std::memset(&addr, 0, sizeof(addr));
221 0 : addr.can_family = AF_CAN;
222 0 : addr.can_ifindex = ifr.ifr_ifindex;
223 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): POSIX bind() requires sockaddr*
224 0 : error = bind(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
225 0 : if (error < 0)
226 : {
227 0 : Logger::error(
228 : CAN, "[SocketCanTransceiver] Failed to bind socket (node=%s, error=%d)", name, error);
229 0 : return;
230 : }
231 :
232 0 : _fileDescriptor = fd;
233 : // NOLINTEND(cppcoreguidelines-pro-type-vararg)
234 : }
235 :
236 0 : void SocketCanTransceiver::guardedClose()
237 : {
238 0 : ::close(_fileDescriptor);
239 0 : _fileDescriptor = -1;
240 0 : }
241 :
242 0 : void SocketCanTransceiver::guardedRun(int maxSentPerRun, int maxReceivedPerRun)
243 : {
244 : // MUTED condition does not affect the messages already in the write queue;
245 : // the idea is that once we confirmed that we had accepted the message for delivery,
246 : // we shall try to deliver it.
247 0 : for (int count = 0; count < maxSentPerRun; ++count)
248 : {
249 0 : auto memory = _txReader.peek();
250 0 : if (memory.size() < TX_ELEMENT_SIZE_BYTES)
251 : {
252 0 : break;
253 : }
254 0 : CANFrame canFrame;
255 0 : ::std::memcpy(static_cast<void*>(&canFrame), memory.data(), sizeof(canFrame));
256 0 : ICANFrameSentListener* listener = nullptr;
257 0 : ::std::memcpy(
258 0 : static_cast<void*>(&listener), memory.data() + sizeof(canFrame), sizeof(void*));
259 0 : _txReader.release();
260 : can_frame socketCanFrame;
261 0 : ::std::memset(&socketCanFrame, 0, sizeof(socketCanFrame));
262 0 : socketCanFrame.can_id = canFrame.getId();
263 0 : int length = canFrame.getPayloadLength();
264 0 : socketCanFrame.can_dlc = length;
265 0 : ::std::memcpy(socketCanFrame.data, canFrame.getPayload(), length);
266 0 : ::std::memset(socketCanFrame.data + length, 0, sizeof(socketCanFrame.data) - length);
267 0 : ssize_t const bytesWritten = ::write(_fileDescriptor, &socketCanFrame, CAN_MTU);
268 0 : if (bytesWritten != CAN_MTU)
269 : {
270 0 : break;
271 : }
272 0 : if (listener != nullptr)
273 : {
274 0 : listener->canFrameSent(canFrame);
275 : }
276 0 : notifySentListeners(canFrame);
277 : }
278 :
279 0 : for (int count = 0; count < maxReceivedPerRun; ++count)
280 : {
281 : alignas(can_frame) uint8_t buffer[CANFD_MTU];
282 0 : ssize_t const length = read(_fileDescriptor, buffer, CANFD_MTU);
283 0 : if (length < 0)
284 : {
285 0 : break;
286 : }
287 0 : if (length == CAN_MTU)
288 : {
289 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
290 0 : can_frame const& socketCanFrame = *reinterpret_cast<can_frame const*>(buffer);
291 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): Logger API is variadic by design.
292 0 : Logger::debug(
293 : CAN,
294 : "[SocketCanTransceiver] received CAN frame, id=0x%X, length=%d",
295 0 : static_cast<int>(::can::CanId::rawId(socketCanFrame.can_id)),
296 0 : static_cast<int>(socketCanFrame.can_dlc));
297 0 : CANFrame canFrame;
298 0 : canFrame.setId(socketCanFrame.can_id);
299 0 : canFrame.setPayload(socketCanFrame.data, socketCanFrame.can_dlc);
300 0 : canFrame.setPayloadLength(socketCanFrame.can_dlc);
301 0 : canFrame.setTimestamp(0);
302 :
303 0 : notifyListeners(canFrame);
304 : }
305 : }
306 0 : }
307 :
308 : } // namespace can
|