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 : if (_config.enableCanFd)
197 : {
198 0 : int const enable_canfd = 1;
199 0 : error = setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &enable_canfd, sizeof(enable_canfd));
200 0 : if (error < 0)
201 : {
202 0 : Logger::error(
203 : CAN,
204 : "[SocketCanTransceiver] Failed to setsockopt socket (node=%s, error=%d)",
205 : name,
206 : error);
207 0 : return;
208 : }
209 : }
210 :
211 0 : error = fcntl(fd, F_SETFL, O_NONBLOCK);
212 0 : if (error < 0)
213 : {
214 0 : Logger::error(
215 : CAN,
216 : "[SocketCanTransceiver] Failed to switch to non-blocking mode (node=%s, error=%d)",
217 : name,
218 : error);
219 0 : return;
220 : }
221 :
222 : struct sockaddr_can addr;
223 0 : ::std::memset(&addr, 0, sizeof(addr));
224 0 : addr.can_family = AF_CAN;
225 0 : addr.can_ifindex = ifr.ifr_ifindex;
226 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): POSIX bind() requires sockaddr*
227 0 : error = bind(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
228 0 : if (error < 0)
229 : {
230 0 : Logger::error(
231 : CAN, "[SocketCanTransceiver] Failed to bind socket (node=%s, error=%d)", name, error);
232 0 : return;
233 : }
234 :
235 0 : _fileDescriptor = fd;
236 : // NOLINTEND(cppcoreguidelines-pro-type-vararg)
237 : }
238 :
239 0 : void SocketCanTransceiver::guardedClose()
240 : {
241 0 : ::close(_fileDescriptor);
242 0 : _fileDescriptor = -1;
243 0 : }
244 :
245 0 : void SocketCanTransceiver::guardedRun(int maxSentPerRun, int maxReceivedPerRun)
246 : {
247 : // MUTED condition does not affect the messages already in the write queue;
248 : // the idea is that once we confirmed that we had accepted the message for delivery,
249 : // we shall try to deliver it.
250 0 : for (int count = 0; count < maxSentPerRun; ++count)
251 : {
252 0 : auto memory = _txReader.peek();
253 0 : if (memory.size() < TX_ELEMENT_SIZE_BYTES)
254 : {
255 0 : break;
256 : }
257 0 : CANFrame canFrame;
258 0 : ::std::memcpy(static_cast<void*>(&canFrame), memory.data(), sizeof(canFrame));
259 0 : ICANFrameSentListener* listener = nullptr;
260 0 : ::std::memcpy(
261 0 : static_cast<void*>(&listener), memory.data() + sizeof(canFrame), sizeof(void*));
262 0 : _txReader.release();
263 :
264 0 : uint8_t const length = static_cast<uint8_t>(canFrame.getPayloadLength());
265 0 : bool const sendAsFd = _config.enableCanFd;
266 :
267 : // canfd_frame is layout-compatible with can_frame for the fields we set
268 : // (can_id, len, data); byte 5 (flags) maps to can_frame::__pad for
269 : // classical frames and must remain 0, which memset guarantees.
270 : canfd_frame outFrame;
271 0 : ::std::memset(&outFrame, 0, sizeof(outFrame));
272 0 : outFrame.can_id = canFrame.getId();
273 0 : outFrame.len = length;
274 0 : if (sendAsFd && _config.enableBitRateSwitch)
275 : {
276 0 : outFrame.flags |= CANFD_BRS;
277 : }
278 0 : ::std::memcpy(outFrame.data, canFrame.getPayload(), length);
279 :
280 : // MTU selects the on-wire frame type: CAN_MTU for classical, CANFD_MTU for FD.
281 0 : size_t const mtu = sendAsFd ? CANFD_MTU : CAN_MTU;
282 0 : ssize_t const bytesWritten = ::write(_fileDescriptor, &outFrame, mtu);
283 0 : if (bytesWritten != static_cast<ssize_t>(mtu))
284 : {
285 0 : break;
286 : }
287 0 : if (listener != nullptr)
288 : {
289 0 : listener->canFrameSent(canFrame);
290 : }
291 0 : notifySentListeners(canFrame);
292 : }
293 :
294 0 : for (int count = 0; count < maxReceivedPerRun; ++count)
295 : {
296 : canfd_frame inFrame;
297 0 : ::std::memset(&inFrame, 0, sizeof(inFrame));
298 0 : ssize_t const bytesRead = ::read(_fileDescriptor, &inFrame, CANFD_MTU);
299 0 : if (bytesRead < 0)
300 : {
301 0 : break;
302 : }
303 0 : if (bytesRead != CAN_MTU && bytesRead != CANFD_MTU)
304 : {
305 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg): Logger API is variadic by design.
306 0 : Logger::warn(
307 : CAN,
308 : "[SocketCanTransceiver] discarded frame with unexpected size=%d",
309 : static_cast<int>(bytesRead));
310 0 : continue;
311 : }
312 :
313 0 : CANFrame canFrame;
314 0 : canFrame.setId(inFrame.can_id);
315 0 : canFrame.setPayload(inFrame.data, inFrame.len);
316 0 : canFrame.setTimestamp(0);
317 :
318 0 : notifyListeners(canFrame);
319 : }
320 0 : }
321 :
322 : } // namespace can
|