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 <unistd.h>
24 :
25 : #include <etl/char_traits.h>
26 : #include <etl/error_handler.h>
27 : #include <etl/memory.h>
28 : #include <etl/span.h>
29 : #include <etl/type_traits.h>
30 : #include <etl/utility.h>
31 : #include <sys/types.h>
32 :
33 : static_assert(
34 : ::etl::is_standard_layout<::can::CANFrame>::value
35 : && ::etl::is_trivially_destructible<::can::CANFrame>::value,
36 : "check for UB while passing through TxQueue");
37 :
38 : namespace can
39 : {
40 :
41 : using ::util::logger::CAN;
42 : using ::util::logger::Logger;
43 :
44 : namespace
45 : {
46 :
47 : template<typename F>
48 0 : void signalGuarded(F&& function)
49 : {
50 : sigset_t set, oldSet;
51 0 : sigfillset(&set);
52 0 : pthread_sigmask(SIG_SETMASK, &set, &oldSet);
53 0 : ::etl::forward<F>(function)();
54 0 : pthread_sigmask(SIG_SETMASK, &oldSet, nullptr);
55 0 : }
56 :
57 : } // namespace
58 :
59 : // needed if ODR-used
60 : size_t const SocketCanTransceiver::TX_QUEUE_SIZE_BYTES;
61 :
62 1 : SocketCanTransceiver::SocketCanTransceiver(DeviceConfig const& config)
63 1 : : AbstractCANTransceiver(config.busId)
64 1 : , _txQueue()
65 1 : , _txReader(_txQueue)
66 1 : , _txWriter(_txQueue)
67 1 : , _config(config)
68 1 : , _fileDescriptor(-1)
69 2 : , _writable(false)
70 1 : {}
71 :
72 0 : ICanTransceiver::ErrorCode SocketCanTransceiver::init()
73 : {
74 0 : if (!isInState(State::CLOSED))
75 : {
76 0 : return ErrorCode::CAN_ERR_ILLEGAL_STATE;
77 : }
78 0 : setState(State::INITIALIZED);
79 0 : return ErrorCode::CAN_ERR_OK;
80 : }
81 :
82 0 : ICanTransceiver::ErrorCode SocketCanTransceiver::open()
83 : {
84 0 : if (!isInState(State::INITIALIZED))
85 : {
86 0 : return ErrorCode::CAN_ERR_ILLEGAL_STATE;
87 : }
88 0 : signalGuarded([this] { guardedOpen(); });
89 0 : setState(State::OPEN);
90 0 : _writable.store(true);
91 0 : return ErrorCode::CAN_ERR_OK;
92 : }
93 :
94 0 : ICanTransceiver::ErrorCode SocketCanTransceiver::open(CANFrame const& /* frame */)
95 : {
96 0 : ETL_ASSERT_FAIL(ETL_ERROR_GENERIC("not implemented"));
97 : return ErrorCode::CAN_ERR_ILLEGAL_STATE;
98 : }
99 :
100 0 : ICanTransceiver::ErrorCode SocketCanTransceiver::close()
101 : {
102 0 : if (!isInState(State::OPEN) && !isInState(State::MUTED))
103 : {
104 0 : return ErrorCode::CAN_ERR_ILLEGAL_STATE;
105 : }
106 0 : signalGuarded([this] { guardedClose(); });
107 0 : _writable.store(false);
108 0 : setState(State::CLOSED);
109 0 : return ErrorCode::CAN_ERR_OK;
110 : }
111 :
112 0 : void SocketCanTransceiver::shutdown() {}
113 :
114 0 : ICanTransceiver::ErrorCode SocketCanTransceiver::write(CANFrame const& frame)
115 : {
116 0 : return writeImpl(frame, nullptr);
117 : }
118 :
119 : ICanTransceiver::ErrorCode
120 0 : SocketCanTransceiver::write(CANFrame const& frame, ICANFrameSentListener& listener)
121 : {
122 0 : return writeImpl(frame, &listener);
123 : }
124 :
125 : ICanTransceiver::ErrorCode
126 0 : SocketCanTransceiver::writeImpl(CANFrame const& frame, ICANFrameSentListener* listener)
127 : {
128 0 : if (!_writable.load(::etl::memory_order_relaxed))
129 : {
130 0 : return ErrorCode::CAN_ERR_ILLEGAL_STATE;
131 : }
132 0 : auto memory = _txWriter.allocate(TX_ELEMENT_SIZE_BYTES);
133 0 : if (memory.size() < TX_ELEMENT_SIZE_BYTES)
134 : {
135 0 : return ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL;
136 : }
137 : // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast): byte-wise copy into the TxQueue
138 0 : ::etl::mem_copy(reinterpret_cast<uint8_t const*>(&frame), sizeof(frame), memory.data());
139 0 : ::etl::mem_copy(
140 0 : reinterpret_cast<uint8_t const*>(&listener), sizeof(void*), memory.data() + sizeof(frame));
141 : // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
142 0 : _txWriter.commit();
143 0 : return ErrorCode::CAN_ERR_OK;
144 : }
145 :
146 0 : ICanTransceiver::ErrorCode SocketCanTransceiver::mute()
147 : {
148 0 : if (!isInState(State::OPEN))
149 : {
150 0 : return ErrorCode::CAN_ERR_ILLEGAL_STATE;
151 : }
152 0 : _writable.store(false);
153 0 : setState(State::MUTED);
154 0 : return ErrorCode::CAN_ERR_OK;
155 : }
156 :
157 0 : ICanTransceiver::ErrorCode SocketCanTransceiver::unmute()
158 : {
159 0 : if (!isInState(State::MUTED))
160 : {
161 0 : return ErrorCode::CAN_ERR_ILLEGAL_STATE;
162 : }
163 0 : setState(State::OPEN);
164 0 : _writable.store(true);
165 0 : return ErrorCode::CAN_ERR_OK;
166 : }
167 :
168 0 : uint32_t SocketCanTransceiver::getBaudrate() const { return 500000U; }
169 :
170 0 : uint16_t SocketCanTransceiver::getHwQueueTimeout() const { return 1U; }
171 :
172 0 : void SocketCanTransceiver::run(int maxSentPerRun, int maxReceivedPerRun)
173 : {
174 0 : signalGuarded([this, maxSentPerRun, maxReceivedPerRun]
175 0 : { guardedRun(maxSentPerRun, maxReceivedPerRun); });
176 0 : }
177 :
178 0 : void SocketCanTransceiver::guardedOpen()
179 : {
180 : // NOLINTBEGIN(cppcoreguidelines-pro-type-vararg): Logger API is variadic by design.
181 0 : char const* const name = _config.name;
182 0 : int error = 0;
183 0 : int const fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
184 0 : if (fd < 0)
185 : {
186 0 : Logger::error(
187 : CAN, "[SocketCanTransceiver] Failed to create socket (node=%s, error=%d)", name, fd);
188 0 : return;
189 : }
190 :
191 : struct ifreq ifr;
192 0 : etl::strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name) / sizeof(ifr.ifr_name[0]));
193 0 : error = ioctl(fd, SIOCGIFINDEX, &ifr);
194 0 : if (error < 0)
195 : {
196 0 : Logger::error(
197 : CAN, "[SocketCanTransceiver] Failed to ioctl socket (node=%s, error=%d)", name, error);
198 0 : return;
199 : }
200 :
201 0 : if (_config.enableCanFd)
202 : {
203 0 : int const enable_canfd = 1;
204 0 : error = setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &enable_canfd, sizeof(enable_canfd));
205 0 : if (error < 0)
206 : {
207 0 : Logger::error(
208 : CAN,
209 : "[SocketCanTransceiver] Failed to setsockopt socket (node=%s, error=%d)",
210 : name,
211 : error);
212 0 : return;
213 : }
214 : }
215 :
216 0 : error = fcntl(fd, F_SETFL, O_NONBLOCK);
217 0 : if (error < 0)
218 : {
219 0 : Logger::error(
220 : CAN,
221 : "[SocketCanTransceiver] Failed to switch to non-blocking mode (node=%s, error=%d)",
222 : name,
223 : error);
224 0 : return;
225 : }
226 :
227 : struct sockaddr_can addr;
228 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): byte-wise zero-fill
229 0 : ::etl::mem_set(reinterpret_cast<uint8_t*>(&addr), sizeof(addr), static_cast<uint8_t>(0));
230 0 : addr.can_family = AF_CAN;
231 0 : addr.can_ifindex = ifr.ifr_ifindex;
232 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): POSIX bind() requires sockaddr*
233 0 : error = bind(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
234 0 : if (error < 0)
235 : {
236 0 : Logger::error(
237 : CAN, "[SocketCanTransceiver] Failed to bind socket (node=%s, error=%d)", name, error);
238 0 : return;
239 : }
240 :
241 0 : _fileDescriptor = fd;
242 : // NOLINTEND(cppcoreguidelines-pro-type-vararg)
243 : }
244 :
245 0 : void SocketCanTransceiver::guardedClose()
246 : {
247 0 : ::close(_fileDescriptor);
248 0 : _fileDescriptor = -1;
249 0 : }
250 :
251 0 : void SocketCanTransceiver::guardedRun(int maxSentPerRun, int maxReceivedPerRun)
252 : {
253 : // MUTED condition does not affect the messages already in the write queue;
254 : // the idea is that once we confirmed that we had accepted the message for delivery,
255 : // we shall try to deliver it.
256 0 : for (int count = 0; count < maxSentPerRun; ++count)
257 : {
258 0 : auto memory = _txReader.peek();
259 0 : if (memory.size() < TX_ELEMENT_SIZE_BYTES)
260 : {
261 0 : break;
262 : }
263 0 : CANFrame canFrame;
264 : // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast): byte-wise copy from the TxQueue
265 0 : ::etl::mem_copy(memory.data(), sizeof(canFrame), reinterpret_cast<uint8_t*>(&canFrame));
266 0 : ICANFrameSentListener* listener = nullptr;
267 0 : ::etl::mem_copy(
268 0 : memory.data() + sizeof(canFrame), sizeof(void*), reinterpret_cast<uint8_t*>(&listener));
269 : // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
270 0 : _txReader.release();
271 :
272 0 : uint8_t const length = static_cast<uint8_t>(canFrame.getPayloadLength());
273 0 : bool const sendAsFd = _config.enableCanFd;
274 :
275 : // canfd_frame is layout-compatible with can_frame for the fields we set
276 : // (can_id, len, data); byte 5 (flags) maps to can_frame::__pad for
277 : // classical frames and must remain 0, which the zero-fill guarantees.
278 : canfd_frame outFrame;
279 0 : ::etl::mem_set(
280 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): byte-wise zero-fill
281 : reinterpret_cast<uint8_t*>(&outFrame),
282 : sizeof(outFrame),
283 : static_cast<uint8_t>(0));
284 0 : outFrame.can_id = canFrame.getId();
285 0 : outFrame.len = length;
286 0 : if (sendAsFd && _config.enableBitRateSwitch)
287 : {
288 0 : outFrame.flags |= CANFD_BRS;
289 : }
290 0 : ::etl::mem_copy(canFrame.getPayload(), length, outFrame.data);
291 :
292 : // MTU selects the on-wire frame type: CAN_MTU for classical, CANFD_MTU for FD.
293 0 : size_t const mtu = sendAsFd ? CANFD_MTU : CAN_MTU;
294 0 : ssize_t const bytesWritten = ::write(_fileDescriptor, &outFrame, mtu);
295 0 : if (bytesWritten != static_cast<ssize_t>(mtu))
296 : {
297 0 : break;
298 : }
299 0 : if (listener != nullptr)
300 : {
301 0 : listener->canFrameSent(canFrame);
302 : }
303 0 : notifySentListeners(canFrame);
304 : }
305 :
306 0 : for (int count = 0; count < maxReceivedPerRun; ++count)
307 : {
308 : canfd_frame inFrame;
309 0 : ::etl::mem_set(
310 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): byte-wise zero-fill
311 : reinterpret_cast<uint8_t*>(&inFrame),
312 : sizeof(inFrame),
313 : static_cast<uint8_t>(0));
314 0 : ssize_t const bytesRead = ::read(_fileDescriptor, &inFrame, CANFD_MTU);
315 0 : if (bytesRead < 0)
316 : {
317 0 : break;
318 : }
319 0 : if (bytesRead != CAN_MTU && bytesRead != CANFD_MTU)
320 : {
321 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): Logger API is variadic by design.
322 0 : Logger::warn(
323 : CAN,
324 : "[SocketCanTransceiver] discarded frame with unexpected size=%d",
325 : static_cast<int>(bytesRead));
326 0 : continue;
327 : }
328 :
329 0 : CANFrame canFrame;
330 0 : canFrame.setId(inFrame.can_id);
331 0 : canFrame.setPayload(inFrame.data, inFrame.len);
332 0 : canFrame.setTimestamp(0);
333 :
334 0 : notifyListeners(canFrame);
335 : }
336 0 : }
337 :
338 : } // namespace can
|