| 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 |  |