1 | // Copyright (C) 2017 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "socketcanbackend.h" |
5 | |
6 | #include "libsocketcan.h" |
7 | |
8 | #include <QtSerialBus/qcanbusdevice.h> |
9 | |
10 | #include <QtCore/qdatastream.h> |
11 | #include <QtCore/qdebug.h> |
12 | #include <QtCore/qdiriterator.h> |
13 | #include <QtCore/qfile.h> |
14 | #include <QtCore/qloggingcategory.h> |
15 | #include <QtCore/qsocketnotifier.h> |
16 | |
17 | #include <linux/can/error.h> |
18 | #include <linux/can/raw.h> |
19 | #include <linux/sockios.h> |
20 | #include <errno.h> |
21 | #include <unistd.h> |
22 | #include <net/if.h> |
23 | #include <sys/ioctl.h> |
24 | #include <sys/time.h> |
25 | |
26 | #ifndef CANFD_BRS |
27 | # define CANFD_BRS 0x01 /* bit rate switch (second bitrate for payload data) */ |
28 | #endif |
29 | #ifndef CANFD_ESI |
30 | # define CANFD_ESI 0x02 /* error state indicator of the transmitting node */ |
31 | #endif |
32 | |
33 | QT_BEGIN_NAMESPACE |
34 | |
35 | Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_SOCKETCAN) |
36 | |
37 | const char sysClassNetC[] = "/sys/class/net/" ; |
38 | const char interfaceC[] = "/device/interface" ; |
39 | const char devIdC[] = "/dev_id" ; |
40 | const char flagsC[] = "/flags" ; |
41 | const char mtuC[] = "/mtu" ; |
42 | const char typeC[] = "/type" ; |
43 | const char virtualC[] = "virtual" ; |
44 | |
45 | enum { |
46 | CanFlexibleDataRateMtu = 72, |
47 | TypeSocketCan = 280, |
48 | DeviceIsActive = 1 |
49 | }; |
50 | |
51 | static QByteArray fileContent(const QString &fileName) |
52 | { |
53 | QFile file(fileName); |
54 | if (!file.open(flags: QIODevice::ReadOnly)) |
55 | return QByteArray(); |
56 | |
57 | return file.readAll().trimmed(); |
58 | } |
59 | |
60 | static bool isFlexibleDataRateCapable(const QString &canDevice) |
61 | { |
62 | const QString path = QLatin1String(sysClassNetC) + canDevice + QLatin1String(mtuC); |
63 | const int mtu = fileContent(fileName: path).toInt(); |
64 | return mtu == CanFlexibleDataRateMtu; |
65 | } |
66 | |
67 | static bool isVirtual(const QString &canDevice) |
68 | { |
69 | const QFileInfo fi(QLatin1String(sysClassNetC) + canDevice); |
70 | return fi.canonicalPath().contains(s: QLatin1String(virtualC)); |
71 | } |
72 | |
73 | static quint32 flags(const QString &canDevice) |
74 | { |
75 | const QString path = QLatin1String(sysClassNetC) + canDevice + QLatin1String(flagsC); |
76 | const quint32 result = fileContent(fileName: path).toUInt(ok: nullptr, base: 0); |
77 | return result; |
78 | } |
79 | |
80 | static QString deviceDescription(const QString &canDevice) |
81 | { |
82 | const QString path = QLatin1String(sysClassNetC) + canDevice + QLatin1String(interfaceC); |
83 | const QByteArray content = fileContent(fileName: path); |
84 | if (content.isEmpty() && isVirtual(canDevice)) |
85 | return QStringLiteral("Virtual CAN" ); |
86 | |
87 | return QString::fromUtf8(ba: content); |
88 | } |
89 | |
90 | static int deviceChannel(const QString &canDevice) |
91 | { |
92 | const QString path = QLatin1String(sysClassNetC) + canDevice + QLatin1String(devIdC); |
93 | const QByteArray content = fileContent(fileName: path); |
94 | return content.toInt(ok: nullptr, base: 0); |
95 | } |
96 | |
97 | QCanBusDeviceInfo SocketCanBackend::socketCanDeviceInfo(const QString &deviceName) |
98 | { |
99 | const QString serial; // exists for code readability purposes only |
100 | const QString alias; // exists for code readability purposes only |
101 | const QString description = deviceDescription(canDevice: deviceName); |
102 | const int channel = deviceChannel(canDevice: deviceName); |
103 | return createDeviceInfo(QStringLiteral("socketcan" ), name: deviceName, |
104 | serialNumber: serial, description, |
105 | alias, channel, isVirtual: isVirtual(canDevice: deviceName), |
106 | isFlexibleDataRateCapable: isFlexibleDataRateCapable(canDevice: deviceName)); |
107 | } |
108 | |
109 | QList<QCanBusDeviceInfo> SocketCanBackend::interfaces() |
110 | { |
111 | QList<QCanBusDeviceInfo> result; |
112 | QDirIterator it(sysClassNetC, |
113 | QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, |
114 | QDirIterator::Subdirectories); |
115 | |
116 | while (it.hasNext()) { |
117 | const QString dirEntry = it.next(); |
118 | if (fileContent(fileName: dirEntry + QLatin1String(typeC)).toInt() != TypeSocketCan) |
119 | continue; |
120 | |
121 | const QString deviceName = dirEntry.mid(position: strlen(s: sysClassNetC)); |
122 | if (!(flags(canDevice: deviceName) & DeviceIsActive)) |
123 | continue; |
124 | |
125 | result.append(t: socketCanDeviceInfo(deviceName)); |
126 | } |
127 | |
128 | std::sort(first: result.begin(), last: result.end(), |
129 | comp: [](const QCanBusDeviceInfo &a, const QCanBusDeviceInfo &b) { |
130 | return a.name() < b.name(); |
131 | }); |
132 | |
133 | return result; |
134 | } |
135 | |
136 | QCanBusDeviceInfo SocketCanBackend::deviceInfo() const |
137 | { |
138 | return socketCanDeviceInfo(deviceName: canSocketName); |
139 | } |
140 | |
141 | SocketCanBackend::SocketCanBackend(const QString &name) : |
142 | canSocketName(name) |
143 | { |
144 | QString errorString; |
145 | libSocketCan.reset(p: new LibSocketCan(&errorString)); |
146 | if (Q_UNLIKELY(!errorString.isEmpty())) { |
147 | qCInfo(QT_CANBUS_PLUGINS_SOCKETCAN, |
148 | "Cannot load library libsocketcan, some functionality will not be available.\n%ls" , |
149 | qUtf16Printable(errorString)); |
150 | } |
151 | |
152 | resetConfigurations(); |
153 | } |
154 | |
155 | SocketCanBackend::~SocketCanBackend() |
156 | { |
157 | close(); |
158 | } |
159 | |
160 | void SocketCanBackend::resetConfigurations() |
161 | { |
162 | QCanBusDevice::setConfigurationParameter( |
163 | key: QCanBusDevice::LoopbackKey, value: true); |
164 | QCanBusDevice::setConfigurationParameter( |
165 | key: QCanBusDevice::ReceiveOwnKey, value: false); |
166 | QCanBusDevice::setConfigurationParameter( |
167 | key: QCanBusDevice::ErrorFilterKey, |
168 | value: QVariant::fromValue(value: QCanBusFrame::FrameErrors(QCanBusFrame::AnyError))); |
169 | QCanBusDevice::setConfigurationParameter( |
170 | key: QCanBusDevice::CanFdKey, value: false); |
171 | QCanBusDevice::setConfigurationParameter( |
172 | key: QCanBusDevice::BitRateKey, value: 500000); |
173 | } |
174 | |
175 | bool SocketCanBackend::open() |
176 | { |
177 | if (canSocket == -1) { |
178 | if (!connectSocket()) { |
179 | close(); // sets UnconnectedState |
180 | return false; |
181 | } |
182 | } |
183 | |
184 | setState(QCanBusDevice::ConnectedState); |
185 | return true; |
186 | } |
187 | |
188 | void SocketCanBackend::close() |
189 | { |
190 | ::close(fd: canSocket); |
191 | canSocket = -1; |
192 | |
193 | setState(QCanBusDevice::UnconnectedState); |
194 | } |
195 | |
196 | bool SocketCanBackend::applyConfigurationParameter(ConfigurationKey key, const QVariant &value) |
197 | { |
198 | bool success = false; |
199 | |
200 | switch (key) { |
201 | case QCanBusDevice::LoopbackKey: |
202 | { |
203 | const int loopback = value.toBool() ? 1 : 0; |
204 | if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_LOOPBACK, |
205 | &loopback, sizeof(loopback)) < 0)) { |
206 | setError(errorText: qt_error_string(errno), |
207 | QCanBusDevice::CanBusError::ConfigurationError); |
208 | break; |
209 | } |
210 | success = true; |
211 | break; |
212 | } |
213 | case QCanBusDevice::ReceiveOwnKey: |
214 | { |
215 | const int receiveOwnMessages = value.toBool() ? 1 : 0; |
216 | if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, |
217 | &receiveOwnMessages, sizeof(receiveOwnMessages)) < 0)) { |
218 | setError(errorText: qt_error_string(errno), |
219 | QCanBusDevice::CanBusError::ConfigurationError); |
220 | break; |
221 | } |
222 | success = true; |
223 | break; |
224 | } |
225 | case QCanBusDevice::ErrorFilterKey: |
226 | { |
227 | const int errorMask = value.value<QCanBusFrame::FrameErrors>(); |
228 | if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, |
229 | &errorMask, sizeof(errorMask)) < 0)) { |
230 | setError(errorText: qt_error_string(errno), |
231 | QCanBusDevice::CanBusError::ConfigurationError); |
232 | break; |
233 | } |
234 | success = true; |
235 | break; |
236 | } |
237 | case QCanBusDevice::RawFilterKey: |
238 | { |
239 | const QList<QCanBusDevice::Filter> filterList |
240 | = value.value<QList<QCanBusDevice::Filter> >(); |
241 | if (!value.isValid() || filterList.isEmpty()) { |
242 | // permit every frame - no restrictions (filter reset) |
243 | can_filter filters = {.can_id: 0, .can_mask: 0}; |
244 | socklen_t s = sizeof(can_filter); |
245 | if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_FILTER, |
246 | &filters, s) != 0)) { |
247 | qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Cannot unset socket filters." ); |
248 | setError(errorText: qt_error_string(errno), |
249 | QCanBusDevice::CanBusError::ConfigurationError); |
250 | break; |
251 | } |
252 | success = true; |
253 | break; |
254 | } |
255 | |
256 | QList<can_filter> filters; |
257 | filters.resize(size: filterList.size()); |
258 | for (int i = 0; i < filterList.size(); i++) { |
259 | const QCanBusDevice::Filter f = filterList.at(i); |
260 | can_filter filter = { .can_id: f.frameId, .can_mask: f.frameIdMask }; |
261 | |
262 | // frame type filter |
263 | switch (f.type) { |
264 | default: |
265 | // any other type cannot be filtered upon |
266 | setError(errorText: tr(s: "Cannot set filter for frame type: %1" ).arg(a: f.type), |
267 | QCanBusDevice::CanBusError::ConfigurationError); |
268 | return false; |
269 | case QCanBusFrame::InvalidFrame: |
270 | break; |
271 | case QCanBusFrame::DataFrame: |
272 | filter.can_mask |= CAN_RTR_FLAG; |
273 | break; |
274 | case QCanBusFrame::ErrorFrame: |
275 | filter.can_mask |= CAN_ERR_FLAG; |
276 | filter.can_id |= CAN_ERR_FLAG; |
277 | break; |
278 | case QCanBusFrame::RemoteRequestFrame: |
279 | filter.can_mask |= CAN_RTR_FLAG; |
280 | filter.can_id |= CAN_RTR_FLAG; |
281 | break; |
282 | } |
283 | |
284 | // frame format filter |
285 | if ((f.format & QCanBusDevice::Filter::MatchBaseAndExtendedFormat) |
286 | == QCanBusDevice::Filter::MatchBaseAndExtendedFormat) { |
287 | // nothing |
288 | } else if (f.format & QCanBusDevice::Filter::MatchBaseFormat) { |
289 | filter.can_mask |= CAN_EFF_FLAG; |
290 | } else if (f.format & QCanBusDevice::Filter::MatchExtendedFormat) { |
291 | filter.can_mask |= CAN_EFF_FLAG; |
292 | filter.can_id |= CAN_EFF_FLAG; |
293 | } |
294 | |
295 | filters[i] = filter; |
296 | } |
297 | if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_FILTER, |
298 | filters.constData(), sizeof(filters[0]) * filters.size()) < 0)) { |
299 | setError(errorText: qt_error_string(errno), |
300 | QCanBusDevice::CanBusError::ConfigurationError); |
301 | break; |
302 | } |
303 | success = true; |
304 | break; |
305 | } |
306 | case QCanBusDevice::CanFdKey: |
307 | { |
308 | const int fd_frames = value.toBool() ? 1 : 0; |
309 | if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, |
310 | &fd_frames, sizeof(fd_frames)) < 0)) { |
311 | setError(errorText: qt_error_string(errno), |
312 | QCanBusDevice::CanBusError::ConfigurationError); |
313 | break; |
314 | } |
315 | success = true; |
316 | break; |
317 | } |
318 | case QCanBusDevice::BitRateKey: |
319 | { |
320 | const quint32 bitRate = value.toUInt(); |
321 | success = libSocketCan->setBitrate(interface: canSocketName, bitrate: bitRate); |
322 | break; |
323 | } |
324 | default: |
325 | setError(errorText: tr(s: "Unsupported configuration key: %1" ).arg(a: key), |
326 | QCanBusDevice::CanBusError::ConfigurationError); |
327 | break; |
328 | } |
329 | |
330 | return success; |
331 | } |
332 | |
333 | bool SocketCanBackend::connectSocket() |
334 | { |
335 | struct ifreq interface; |
336 | |
337 | if (Q_UNLIKELY((canSocket = socket(PF_CAN, SOCK_RAW | SOCK_NONBLOCK, protocol)) < 0)) { |
338 | setError(errorText: qt_error_string(errno), |
339 | QCanBusDevice::CanBusError::ConnectionError); |
340 | return false; |
341 | } |
342 | |
343 | qstrncpy(dst: interface.ifr_name, src: canSocketName.toLatin1().constData(), len: sizeof(interface.ifr_name)); |
344 | if (Q_UNLIKELY(ioctl(canSocket, SIOCGIFINDEX, &interface) < 0)) { |
345 | setError(errorText: qt_error_string(errno), |
346 | QCanBusDevice::CanBusError::ConnectionError); |
347 | return false; |
348 | } |
349 | |
350 | m_address.can_family = AF_CAN; |
351 | m_address.can_ifindex = interface.ifr_ifindex; |
352 | |
353 | if (Q_UNLIKELY(bind(canSocket, reinterpret_cast<struct sockaddr *>(&m_address), sizeof(m_address)) < 0)) { |
354 | setError(errorText: qt_error_string(errno), |
355 | QCanBusDevice::CanBusError::ConnectionError); |
356 | return false; |
357 | } |
358 | |
359 | m_iov.iov_base = &m_frame; |
360 | m_msg.msg_name = &m_address; |
361 | m_msg.msg_iov = &m_iov; |
362 | m_msg.msg_iovlen = 1; |
363 | m_msg.msg_control = &m_ctrlmsg; |
364 | |
365 | delete notifier; |
366 | |
367 | notifier = new QSocketNotifier(canSocket, QSocketNotifier::Read, this); |
368 | connect(sender: notifier, signal: &QSocketNotifier::activated, |
369 | context: this, slot: &SocketCanBackend::readSocket); |
370 | |
371 | //apply all stored configurations |
372 | const auto keys = configurationKeys(); |
373 | for (ConfigurationKey key : keys) { |
374 | const QVariant param = configurationParameter(key); |
375 | bool success = applyConfigurationParameter(key, value: param); |
376 | if (Q_UNLIKELY(!success)) { |
377 | qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Cannot apply parameter: %d with value: %ls." , |
378 | key, qUtf16Printable(param.toString())); |
379 | } |
380 | } |
381 | |
382 | return true; |
383 | } |
384 | |
385 | void SocketCanBackend::setConfigurationParameter(ConfigurationKey key, const QVariant &value) |
386 | { |
387 | if (key == QCanBusDevice::RawFilterKey) { |
388 | //verify valid/supported filters |
389 | |
390 | const auto filters = value.value<QList<QCanBusDevice::Filter> >(); |
391 | for (QCanBusDevice::Filter f : filters) { |
392 | switch (f.type) { |
393 | case QCanBusFrame::UnknownFrame: |
394 | |
395 | default: |
396 | setError(errorText: tr(s: "Cannot set filter for frame type: %1" ).arg(a: f.type), |
397 | QCanBusDevice::CanBusError::ConfigurationError); |
398 | return; |
399 | case QCanBusFrame::InvalidFrame: |
400 | case QCanBusFrame::DataFrame: |
401 | case QCanBusFrame::ErrorFrame: |
402 | case QCanBusFrame::RemoteRequestFrame: |
403 | break; |
404 | } |
405 | |
406 | if (f.frameId > 0x1FFFFFFFU) { |
407 | setError(errorText: tr(s: "FrameId %1 larger than 29 bit." ).arg(a: f.frameId), |
408 | QCanBusDevice::CanBusError::ConfigurationError); |
409 | return; |
410 | } |
411 | } |
412 | } else if (key == QCanBusDevice::ProtocolKey) { |
413 | bool ok = false; |
414 | const int newProtocol = value.toInt(ok: &ok); |
415 | if (Q_UNLIKELY(!ok || (newProtocol < 0))) { |
416 | const QString errorString = tr(s: "Cannot set protocol to value %1." ).arg(a: value.toString()); |
417 | setError(errorText: errorString, QCanBusDevice::ConfigurationError); |
418 | qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls" , qUtf16Printable(errorString)); |
419 | return; |
420 | } |
421 | protocol = newProtocol; |
422 | } |
423 | // connected & params not applyable/invalid |
424 | if (canSocket != -1 && !applyConfigurationParameter(key, value)) |
425 | return; |
426 | |
427 | QCanBusDevice::setConfigurationParameter(key, value); |
428 | |
429 | // we need to check CAN FD option a lot -> cache it and avoid QList lookup |
430 | if (key == QCanBusDevice::CanFdKey) |
431 | canFdOptionEnabled = value.toBool(); |
432 | } |
433 | |
434 | bool SocketCanBackend::writeFrame(const QCanBusFrame &newData) |
435 | { |
436 | if (state() != ConnectedState) |
437 | return false; |
438 | |
439 | if (Q_UNLIKELY(!newData.isValid())) { |
440 | setError(errorText: tr(s: "Cannot write invalid QCanBusFrame" ), QCanBusDevice::WriteError); |
441 | return false; |
442 | } |
443 | |
444 | canid_t canId = newData.frameId(); |
445 | if (newData.hasExtendedFrameFormat()) |
446 | canId |= CAN_EFF_FLAG; |
447 | |
448 | if (newData.frameType() == QCanBusFrame::RemoteRequestFrame) { |
449 | canId |= CAN_RTR_FLAG; |
450 | } else if (newData.frameType() == QCanBusFrame::ErrorFrame) { |
451 | canId = static_cast<canid_t>((newData.error() & QCanBusFrame::AnyError)); |
452 | canId |= CAN_ERR_FLAG; |
453 | } |
454 | |
455 | if (Q_UNLIKELY(!canFdOptionEnabled && newData.hasFlexibleDataRateFormat())) { |
456 | const QString error = tr(s: "Cannot write CAN FD frame because CAN FD option is not enabled." ); |
457 | qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls" , qUtf16Printable(error)); |
458 | setError(errorText: error, QCanBusDevice::WriteError); |
459 | return false; |
460 | } |
461 | |
462 | qint64 bytesWritten = 0; |
463 | if (newData.hasFlexibleDataRateFormat()) { |
464 | canfd_frame frame = {}; |
465 | frame.len = newData.payload().size(); |
466 | frame.can_id = canId; |
467 | frame.flags = newData.hasBitrateSwitch() ? CANFD_BRS : 0; |
468 | frame.flags |= newData.hasErrorStateIndicator() ? CANFD_ESI : 0; |
469 | ::memcpy(dest: frame.data, src: newData.payload().constData(), n: frame.len); |
470 | |
471 | bytesWritten = ::write(fd: canSocket, buf: &frame, n: sizeof(frame)); |
472 | } else { |
473 | can_frame frame = {}; |
474 | frame.can_dlc = newData.payload().size(); |
475 | frame.can_id = canId; |
476 | ::memcpy(dest: frame.data, src: newData.payload().constData(), n: frame.can_dlc); |
477 | |
478 | bytesWritten = ::write(fd: canSocket, buf: &frame, n: sizeof(frame)); |
479 | } |
480 | |
481 | if (Q_UNLIKELY(bytesWritten < 0)) { |
482 | setError(errorText: qt_error_string(errno), |
483 | QCanBusDevice::CanBusError::WriteError); |
484 | return false; |
485 | } |
486 | |
487 | emit framesWritten(framesCount: 1); |
488 | |
489 | return true; |
490 | } |
491 | |
492 | QString SocketCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame) |
493 | { |
494 | if (errorFrame.frameType() != QCanBusFrame::ErrorFrame) |
495 | return QString(); |
496 | |
497 | // the payload may contain the error details |
498 | const QByteArray data = errorFrame.payload(); |
499 | QString errorMsg; |
500 | |
501 | if (errorFrame.error() & QCanBusFrame::TransmissionTimeoutError) |
502 | errorMsg += QStringLiteral("TX timeout\n" ); |
503 | |
504 | if (errorFrame.error() & QCanBusFrame::MissingAcknowledgmentError) |
505 | errorMsg += QStringLiteral("Received no ACK on transmission\n" ); |
506 | |
507 | if (errorFrame.error() & QCanBusFrame::BusOffError) |
508 | errorMsg += QStringLiteral("Bus off\n" ); |
509 | |
510 | if (errorFrame.error() & QCanBusFrame::BusError) |
511 | errorMsg += QStringLiteral("Bus error\n" ); |
512 | |
513 | if (errorFrame.error() & QCanBusFrame::ControllerRestartError) |
514 | errorMsg += QStringLiteral("Controller restarted\n" ); |
515 | |
516 | if (errorFrame.error() & QCanBusFrame::UnknownError) |
517 | errorMsg += QStringLiteral("Unknown error\n" ); |
518 | |
519 | if (errorFrame.error() & QCanBusFrame::LostArbitrationError) { |
520 | errorMsg += QStringLiteral("Lost arbitration:\n" ); |
521 | if (data.size() >= 1) { |
522 | errorMsg += QString::number(data.at(i: 0), base: 16); |
523 | errorMsg += QStringLiteral(" bit\n" ); |
524 | } |
525 | } |
526 | |
527 | if (errorFrame.error() & QCanBusFrame::ControllerError) { |
528 | errorMsg += QStringLiteral("Controller problem:\n" ); |
529 | if (data.size() >= 2) { |
530 | char b = data.at(i: 1) ; |
531 | if (b & CAN_ERR_CRTL_RX_OVERFLOW) |
532 | errorMsg += QStringLiteral(" RX buffer overflow\n" ); |
533 | if (b & CAN_ERR_CRTL_TX_OVERFLOW) |
534 | errorMsg += QStringLiteral(" TX buffer overflow\n" ); |
535 | if (b & CAN_ERR_CRTL_RX_WARNING) |
536 | errorMsg += QStringLiteral(" reached warning level for RX errors\n" ); |
537 | if (b & CAN_ERR_CRTL_TX_WARNING) |
538 | errorMsg += QStringLiteral(" reached warning level for TX errors\n" ); |
539 | if (b & CAN_ERR_CRTL_RX_PASSIVE) |
540 | errorMsg += QStringLiteral(" reached error passive status RX\n" ); |
541 | if (b & CAN_ERR_CRTL_TX_PASSIVE) |
542 | errorMsg += QStringLiteral(" reached error passive status TX\n" ); |
543 | |
544 | if (b == CAN_ERR_CRTL_UNSPEC) |
545 | errorMsg += QStringLiteral(" Unspecified error\n" ); |
546 | } |
547 | } |
548 | |
549 | if (errorFrame.error() & QCanBusFrame::TransceiverError) { |
550 | errorMsg = QStringLiteral("Transceiver status:" ); |
551 | if (data.size() >= 5) { |
552 | char b = data.at(i: 4); |
553 | if (b & CAN_ERR_TRX_CANH_NO_WIRE) |
554 | errorMsg += QStringLiteral(" CAN-transceiver CANH no wire\n" ); |
555 | if (b & CAN_ERR_TRX_CANH_SHORT_TO_BAT) |
556 | errorMsg += QStringLiteral(" CAN-transceiver CANH short to bat\n" ); |
557 | if (b & CAN_ERR_TRX_CANH_SHORT_TO_VCC) |
558 | errorMsg += QStringLiteral(" CAN-transceiver CANH short to vcc\n" ); |
559 | if (b & CAN_ERR_TRX_CANH_SHORT_TO_GND) |
560 | errorMsg += QStringLiteral(" CAN-transceiver CANH short to ground\n" ); |
561 | if (b & CAN_ERR_TRX_CANL_NO_WIRE) |
562 | errorMsg += QStringLiteral(" CAN-transceiver CANL no wire\n" ); |
563 | if (b & CAN_ERR_TRX_CANL_SHORT_TO_BAT) |
564 | errorMsg += QStringLiteral(" CAN-transceiver CANL short to bat\n" ); |
565 | if (b & CAN_ERR_TRX_CANL_SHORT_TO_VCC) |
566 | errorMsg += QStringLiteral(" CAN-transceiver CANL short to vcc\n" ); |
567 | if (b & CAN_ERR_TRX_CANL_SHORT_TO_GND) |
568 | errorMsg += QStringLiteral(" CAN-transceiver CANL short to ground\n" ); |
569 | if (b & CAN_ERR_TRX_CANL_SHORT_TO_CANH) |
570 | errorMsg += QStringLiteral(" CAN-transceiver CANL short to CANH\n" ); |
571 | |
572 | if (b == CAN_ERR_TRX_UNSPEC) |
573 | errorMsg += QStringLiteral(" unspecified\n" ); |
574 | } |
575 | |
576 | } |
577 | |
578 | if (errorFrame.error() & QCanBusFrame::ProtocolViolationError) { |
579 | errorMsg += QStringLiteral("Protocol violation:\n" ); |
580 | if (data.size() > 3) { |
581 | char b = data.at(i: 2); |
582 | if (b & CAN_ERR_PROT_BIT) |
583 | errorMsg += QStringLiteral(" single bit error\n" ); |
584 | if (b & CAN_ERR_PROT_FORM) |
585 | errorMsg += QStringLiteral(" frame format error\n" ); |
586 | if (b & CAN_ERR_PROT_STUFF) |
587 | errorMsg += QStringLiteral(" bit stuffing error\n" ); |
588 | if (b & CAN_ERR_PROT_BIT0) |
589 | errorMsg += QStringLiteral(" unable to send dominant bit\n" ); |
590 | if (b & CAN_ERR_PROT_BIT1) |
591 | errorMsg += QStringLiteral(" unable to send recessive bit\n" ); |
592 | if (b & CAN_ERR_PROT_OVERLOAD) |
593 | errorMsg += QStringLiteral(" bus overload\n" ); |
594 | if (b & CAN_ERR_PROT_ACTIVE) |
595 | errorMsg += QStringLiteral(" active error announcement\n" ); |
596 | if (b & CAN_ERR_PROT_TX) |
597 | errorMsg += QStringLiteral(" error occurred on transmission\n" ); |
598 | |
599 | if (b == CAN_ERR_PROT_UNSPEC) |
600 | errorMsg += QStringLiteral(" unspecified\n" ); |
601 | } |
602 | if (data.size() > 4) { |
603 | char b = data.at(i: 3); |
604 | if (b == CAN_ERR_PROT_LOC_SOF) |
605 | errorMsg += QStringLiteral(" start of frame\n" ); |
606 | if (b == CAN_ERR_PROT_LOC_ID28_21) |
607 | errorMsg += QStringLiteral(" ID bits 28 - 21 (SFF: 10 - 3)\n" ); |
608 | if (b == CAN_ERR_PROT_LOC_ID20_18) |
609 | errorMsg += QStringLiteral(" ID bits 20 - 18 (SFF: 2 - 0 )\n" ); |
610 | if (b == CAN_ERR_PROT_LOC_SRTR) |
611 | errorMsg += QStringLiteral(" substitute RTR (SFF: RTR)\n" ); |
612 | if (b == CAN_ERR_PROT_LOC_IDE) |
613 | errorMsg += QStringLiteral(" identifier extension\n" ); |
614 | if (b == CAN_ERR_PROT_LOC_ID17_13) |
615 | errorMsg += QStringLiteral(" ID bits 17-13\n" ); |
616 | if (b == CAN_ERR_PROT_LOC_ID12_05) |
617 | errorMsg += QStringLiteral(" ID bits 12-5\n" ); |
618 | if (b == CAN_ERR_PROT_LOC_ID04_00) |
619 | errorMsg += QStringLiteral(" ID bits 4-0\n" ); |
620 | if (b == CAN_ERR_PROT_LOC_RTR) |
621 | errorMsg += QStringLiteral(" RTR\n" ); |
622 | if (b == CAN_ERR_PROT_LOC_RES1) |
623 | errorMsg += QStringLiteral(" reserved bit 1\n" ); |
624 | if (b == CAN_ERR_PROT_LOC_RES0) |
625 | errorMsg += QStringLiteral(" reserved bit 0\n" ); |
626 | if (b == CAN_ERR_PROT_LOC_DLC) |
627 | errorMsg += QStringLiteral(" data length code\n" ); |
628 | if (b == CAN_ERR_PROT_LOC_DATA) |
629 | errorMsg += QStringLiteral(" data section\n" ); |
630 | if (b == CAN_ERR_PROT_LOC_CRC_SEQ) |
631 | errorMsg += QStringLiteral(" CRC sequence\n" ); |
632 | if (b == CAN_ERR_PROT_LOC_CRC_DEL) |
633 | errorMsg += QStringLiteral(" CRC delimiter\n" ); |
634 | if (b == CAN_ERR_PROT_LOC_ACK) |
635 | errorMsg += QStringLiteral(" ACK slot\n" ); |
636 | if (b == CAN_ERR_PROT_LOC_ACK_DEL) |
637 | errorMsg += QStringLiteral(" ACK delimiter\n" ); |
638 | if (b == CAN_ERR_PROT_LOC_EOF) |
639 | errorMsg += QStringLiteral(" end of frame\n" ); |
640 | if (b == CAN_ERR_PROT_LOC_INTERM) |
641 | errorMsg += QStringLiteral(" Intermission\n" ); |
642 | |
643 | if (b == CAN_ERR_PROT_LOC_UNSPEC) |
644 | errorMsg += QStringLiteral(" unspecified\n" ); |
645 | } |
646 | } |
647 | |
648 | // cut trailing \n |
649 | if (!errorMsg.isEmpty()) |
650 | errorMsg.chop(n: 1); |
651 | |
652 | return errorMsg; |
653 | } |
654 | |
655 | void SocketCanBackend::readSocket() |
656 | { |
657 | QList<QCanBusFrame> newFrames; |
658 | |
659 | for (;;) { |
660 | m_frame = {}; |
661 | m_iov.iov_len = sizeof(m_frame); |
662 | m_msg.msg_namelen = sizeof(m_addr); |
663 | m_msg.msg_controllen = sizeof(m_ctrlmsg); |
664 | m_msg.msg_flags = 0; |
665 | |
666 | const int bytesReceived = ::recvmsg(fd: canSocket, message: &m_msg, flags: 0); |
667 | |
668 | if (bytesReceived <= 0) { |
669 | break; |
670 | } else if (Q_UNLIKELY(bytesReceived != CANFD_MTU && bytesReceived != CAN_MTU)) { |
671 | setError(errorText: tr(s: "ERROR SocketCanBackend: incomplete CAN frame" ), |
672 | QCanBusDevice::CanBusError::ReadError); |
673 | continue; |
674 | } else if (Q_UNLIKELY(m_frame.len > bytesReceived - offsetof(canfd_frame, data))) { |
675 | setError(errorText: tr(s: "ERROR SocketCanBackend: invalid CAN frame length" ), |
676 | QCanBusDevice::CanBusError::ReadError); |
677 | continue; |
678 | } |
679 | |
680 | struct timeval timeStamp = {}; |
681 | if (Q_UNLIKELY(ioctl(canSocket, SIOCGSTAMP, &timeStamp) < 0)) { |
682 | setError(errorText: qt_error_string(errno), |
683 | QCanBusDevice::CanBusError::ReadError); |
684 | timeStamp = {}; |
685 | } |
686 | |
687 | const QCanBusFrame::TimeStamp stamp(timeStamp.tv_sec, timeStamp.tv_usec); |
688 | QCanBusFrame bufferedFrame; |
689 | bufferedFrame.setTimeStamp(stamp); |
690 | bufferedFrame.setFlexibleDataRateFormat(bytesReceived == CANFD_MTU); |
691 | |
692 | bufferedFrame.setExtendedFrameFormat(m_frame.can_id & CAN_EFF_FLAG); |
693 | Q_ASSERT(m_frame.len <= CANFD_MAX_DLEN); |
694 | |
695 | if (m_frame.can_id & CAN_RTR_FLAG) |
696 | bufferedFrame.setFrameType(QCanBusFrame::RemoteRequestFrame); |
697 | if (m_frame.can_id & CAN_ERR_FLAG) |
698 | bufferedFrame.setFrameType(QCanBusFrame::ErrorFrame); |
699 | if (m_frame.flags & CANFD_BRS) |
700 | bufferedFrame.setBitrateSwitch(true); |
701 | if (m_frame.flags & CANFD_ESI) |
702 | bufferedFrame.setErrorStateIndicator(true); |
703 | if (m_msg.msg_flags & MSG_CONFIRM) |
704 | bufferedFrame.setLocalEcho(true); |
705 | |
706 | bufferedFrame.setFrameId(m_frame.can_id & CAN_EFF_MASK); |
707 | |
708 | const QByteArray load(reinterpret_cast<char *>(m_frame.data), m_frame.len); |
709 | bufferedFrame.setPayload(load); |
710 | |
711 | newFrames.append(t: std::move(bufferedFrame)); |
712 | } |
713 | |
714 | enqueueReceivedFrames(newFrames); |
715 | } |
716 | |
717 | void SocketCanBackend::resetController() |
718 | { |
719 | libSocketCan->restart(interface: canSocketName); |
720 | } |
721 | |
722 | bool SocketCanBackend::hasBusStatus() const |
723 | { |
724 | if (isVirtual(canDevice: canSocketName.toLatin1())) |
725 | return false; |
726 | |
727 | return libSocketCan->hasBusStatus(); |
728 | } |
729 | |
730 | QCanBusDevice::CanBusStatus SocketCanBackend::busStatus() |
731 | { |
732 | return libSocketCan->busStatus(interface: canSocketName); |
733 | } |
734 | |
735 | QT_END_NAMESPACE |
736 | |