| 1 | // Copyright (C) 2017 Denis Shienkov <denis.shienkov@gmail.com> | 
| 2 | // Copyright (c) 2020 Andre Hartmann <aha_1980@gmx.de> | 
| 3 | // Copyright (C) 2017 The Qt Company Ltd. | 
| 4 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only | 
| 5 |  | 
| 6 | #include "peakcanbackend.h" | 
| 7 | #include "peakcanbackend_p.h" | 
| 8 | #include "peakcan_symbols_p.h" | 
| 9 |  | 
| 10 | #include <QtSerialBus/qcanbusdevice.h> | 
| 11 |  | 
| 12 | #include <QtCore/qtimer.h> | 
| 13 | #include <QtCore/qcoreevent.h> | 
| 14 | #include <QtCore/qloggingcategory.h> | 
| 15 |  | 
| 16 | #include <algorithm> | 
| 17 | #include <vector> | 
| 18 |  | 
| 19 | #ifdef Q_OS_WIN32 | 
| 20 | #   include <QtCore/qwineventnotifier.h> | 
| 21 | #else | 
| 22 | #   include <QtCore/qsocketnotifier.h> | 
| 23 | #endif | 
| 24 |  | 
| 25 | QT_BEGIN_NAMESPACE | 
| 26 |  | 
| 27 | Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_PEAKCAN) | 
| 28 |  | 
| 29 | #ifndef LINK_LIBPCANBASIC | 
| 30 | Q_GLOBAL_STATIC(QLibrary, pcanLibrary) | 
| 31 | #endif | 
| 32 |  | 
| 33 | bool PeakCanBackend::canCreate(QString *errorReason) | 
| 34 | { | 
| 35 | #ifdef LINK_LIBPCANBASIC | 
| 36 |     Q_UNUSED(errorReason); | 
| 37 | #else | 
| 38 |     static bool symbolsResolved = resolvePeakCanSymbols(pcanLibrary: pcanLibrary()); | 
| 39 |     if (Q_UNLIKELY(!symbolsResolved)) { | 
| 40 |         qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot load library: %ls" , | 
| 41 |                 qUtf16Printable(pcanLibrary()->errorString())); | 
| 42 |         *errorReason = pcanLibrary()->errorString(); | 
| 43 |         return false; | 
| 44 |     } | 
| 45 | #endif | 
| 46 |  | 
| 47 |     char apiVersion[32]; | 
| 48 |     TPCANStatus stat = CAN_GetValue(PCAN_NONEBUS, PCAN_API_VERSION, apiVersion, sizeof(apiVersion)); | 
| 49 |     if (Q_UNLIKELY(stat != PCAN_ERROR_OK)) { | 
| 50 |         qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot resolve PCAN-API version!" ); | 
| 51 |         return false; | 
| 52 |     } | 
| 53 |     qCInfo(QT_CANBUS_PLUGINS_PEAKCAN, "Using PCAN-API version: %s" , apiVersion); | 
| 54 |  | 
| 55 |     return true; | 
| 56 | } | 
| 57 |  | 
| 58 | struct PcanChannel{ | 
| 59 |     char        name[6]; | 
| 60 |     TPCANHandle index; | 
| 61 | }; | 
| 62 | static const PcanChannel pcanChannels[] = { | 
| 63 |     { .name: "usb0" ,  PCAN_USBBUS1  }, | 
| 64 |     { .name: "usb1" ,  PCAN_USBBUS2  }, | 
| 65 |     { .name: "usb2" ,  PCAN_USBBUS3  }, | 
| 66 |     { .name: "usb3" ,  PCAN_USBBUS4  }, | 
| 67 |     { .name: "usb4" ,  PCAN_USBBUS5  }, | 
| 68 |     { .name: "usb5" ,  PCAN_USBBUS6  }, | 
| 69 |     { .name: "usb6" ,  PCAN_USBBUS7  }, | 
| 70 |     { .name: "usb7" ,  PCAN_USBBUS8  }, | 
| 71 |     { .name: "usb8" ,  PCAN_USBBUS9  }, | 
| 72 |     { .name: "usb9" ,  PCAN_USBBUS10 }, | 
| 73 |     { .name: "usb10" , PCAN_USBBUS11 }, | 
| 74 |     { .name: "usb11" , PCAN_USBBUS12 }, | 
| 75 |     { .name: "usb12" , PCAN_USBBUS13 }, | 
| 76 |     { .name: "usb13" , PCAN_USBBUS14 }, | 
| 77 |     { .name: "usb14" , PCAN_USBBUS15 }, | 
| 78 |     { .name: "usb15" , PCAN_USBBUS16 }, | 
| 79 |     { .name: "pci0" ,  PCAN_PCIBUS1  }, | 
| 80 |     { .name: "pci1" ,  PCAN_PCIBUS2  }, | 
| 81 |     { .name: "pci2" ,  PCAN_PCIBUS3  }, | 
| 82 |     { .name: "pci3" ,  PCAN_PCIBUS4  }, | 
| 83 |     { .name: "pci4" ,  PCAN_PCIBUS5  }, | 
| 84 |     { .name: "pci5" ,  PCAN_PCIBUS6  }, | 
| 85 |     { .name: "pci6" ,  PCAN_PCIBUS7  }, | 
| 86 |     { .name: "pci7" ,  PCAN_PCIBUS8  }, | 
| 87 |     { .name: "pci8" ,  PCAN_PCIBUS9  }, | 
| 88 |     { .name: "pci9" ,  PCAN_PCIBUS10 }, | 
| 89 |     { .name: "pci10" , PCAN_PCIBUS11 }, | 
| 90 |     { .name: "pci11" , PCAN_PCIBUS12 }, | 
| 91 |     { .name: "pci12" , PCAN_PCIBUS13 }, | 
| 92 |     { .name: "pci13" , PCAN_PCIBUS14 }, | 
| 93 |     { .name: "pci14" , PCAN_PCIBUS15 }, | 
| 94 |     { .name: "pci15" , PCAN_PCIBUS16 }, | 
| 95 |     { .name: "none" ,  PCAN_NONEBUS  } | 
| 96 | }; | 
| 97 |  | 
| 98 | QList<QCanBusDeviceInfo> PeakCanBackend::interfacesByChannelCondition(Availability available) | 
| 99 | { | 
| 100 |     QList<QCanBusDeviceInfo> result; | 
| 101 |  | 
| 102 |     for (int i = 0; pcanChannels[i].index != PCAN_NONEBUS; ++i) { | 
| 103 |         uint value = 0; | 
| 104 |         const TPCANHandle index = pcanChannels[i].index; | 
| 105 |         const TPCANStatus stat = ::CAN_GetValue(index, PCAN_CHANNEL_CONDITION, | 
| 106 |                                                 &value, sizeof(value)); | 
| 107 |         if ((stat == PCAN_ERROR_OK) && (value & uint(available))) { | 
| 108 |             const TPCANStatus fdStat = ::CAN_GetValue(index, PCAN_CHANNEL_FEATURES, | 
| 109 |                                                       &value, sizeof(value)); | 
| 110 |             const bool isFd = (fdStat == PCAN_ERROR_OK) && (value & FEATURE_FD_CAPABLE); | 
| 111 |  | 
| 112 |             char description[256] = {0}; | 
| 113 |             const TPCANStatus descStat = ::CAN_GetValue(index, PCAN_HARDWARE_NAME, | 
| 114 |                                                         description, sizeof(description)); | 
| 115 |             if (descStat != PCAN_ERROR_OK) | 
| 116 |                 description[0] = 0; | 
| 117 |  | 
| 118 |             int channel = 0; | 
| 119 |             const TPCANStatus chnStat = ::CAN_GetValue(index, PCAN_CONTROLLER_NUMBER, | 
| 120 |                                                        &channel, sizeof(channel)); | 
| 121 |             if (chnStat != PCAN_ERROR_OK) | 
| 122 |                 channel = 0; | 
| 123 |  | 
| 124 |             QString alias; | 
| 125 |             quint32 deviceId = 0; | 
| 126 |             const TPCANStatus idStat = ::CAN_GetValue(index, PCAN_DEVICE_ID, | 
| 127 |                                                       &deviceId, sizeof(deviceId)); | 
| 128 |             if (idStat == PCAN_ERROR_OK) | 
| 129 |                 alias = QString::number(deviceId); | 
| 130 |  | 
| 131 |             result.append(t: QCanBusDevice::createDeviceInfo(QStringLiteral("peakcan" ), | 
| 132 |                                            name: QLatin1String(pcanChannels[i].name), | 
| 133 |                                            serialNumber: QString(), description: QLatin1String(description), | 
| 134 |                                            alias, channel, isVirtual: false, isFlexibleDataRateCapable: isFd)); | 
| 135 |         } | 
| 136 |     } | 
| 137 |  | 
| 138 |     return result; | 
| 139 | } | 
| 140 |  | 
| 141 | static QString pcanChannelNameForIndex(uint index) | 
| 142 | { | 
| 143 |     const auto pcanChannel = std::find_if(first: std::begin(arr: pcanChannels), last: std::end(arr: pcanChannels), | 
| 144 |                                           pred: [index](PcanChannel channel) { | 
| 145 |         return channel.index == index; | 
| 146 |     }); | 
| 147 |     if (Q_LIKELY(pcanChannel != std::end(pcanChannels))) | 
| 148 |         return pcanChannel->name; | 
| 149 |  | 
| 150 |     qWarning(msg: "%s: Cannot get channel name for index %u." , Q_FUNC_INFO, index); | 
| 151 |     return QStringLiteral("none" ); | 
| 152 | } | 
| 153 |  | 
| 154 | QList<QCanBusDeviceInfo> PeakCanBackend::interfacesByAttachedChannels(Availability available, bool *ok) | 
| 155 | { | 
| 156 |     *ok = true; | 
| 157 |     quint32 count = 0; | 
| 158 |     const TPCANStatus countStat = ::CAN_GetValue(0, PCAN_ATTACHED_CHANNELS_COUNT, | 
| 159 |                                                  &count, sizeof(count)); | 
| 160 |     if (Q_UNLIKELY(countStat != PCAN_ERROR_OK)) { | 
| 161 |         qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot query PCAN_ATTACHED_CHANNELS_COUNT." ); | 
| 162 |         *ok = false; | 
| 163 |         return {}; | 
| 164 |     } | 
| 165 |     if (count == 0) | 
| 166 |         return {}; | 
| 167 |  | 
| 168 |     std::vector<TPCANChannelInformation> infos(count); | 
| 169 |     const TPCANStatus infosStat = ::CAN_GetValue(0, PCAN_ATTACHED_CHANNELS, infos.data(), | 
| 170 |                                                  quint32(infos.size() * sizeof(TPCANChannelInformation))); | 
| 171 |     if (Q_UNLIKELY(infosStat != PCAN_ERROR_OK)) { | 
| 172 |         qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot query PCAN_ATTACHED_CHANNELS." ); | 
| 173 |         *ok = false; | 
| 174 |         return {}; | 
| 175 |     } | 
| 176 |  | 
| 177 |     QList<QCanBusDeviceInfo> result; | 
| 178 |     for (quint32 i = 0; i < count; ++i) { | 
| 179 |         auto info = infos[i]; | 
| 180 |         if (info.channel_condition & uint(available)) { | 
| 181 |             const QString name = pcanChannelNameForIndex(index: info.channel_handle); | 
| 182 |             const QString description = info.device_name; | 
| 183 |             const QString alias = QString::number(info.device_id); | 
| 184 |             const int channel = info.controller_number; | 
| 185 |             const bool isCanFd = (info.device_features & FEATURE_FD_CAPABLE); | 
| 186 |  | 
| 187 |             result.append(t: createDeviceInfo(QStringLiteral("peakcan" ), name, serialNumber: QString(), | 
| 188 |                                            description, alias, | 
| 189 |                                            channel, isVirtual: false, isFlexibleDataRateCapable: isCanFd)); | 
| 190 |         } | 
| 191 |     } | 
| 192 |  | 
| 193 |     return result; | 
| 194 | } | 
| 195 |  | 
| 196 | QList<QCanBusDeviceInfo> PeakCanBackend::attachedInterfaces(Availability available) | 
| 197 | { | 
| 198 | #ifdef Q_OS_WIN | 
| 199 |     bool ok = false; | 
| 200 |     QList<QCanBusDeviceInfo> attachedChannelsResult = interfacesByAttachedChannels(available, &ok); | 
| 201 |     if (ok) | 
| 202 |         return attachedChannelsResult; | 
| 203 | #endif | 
| 204 |  | 
| 205 |     QList<QCanBusDeviceInfo> result = interfacesByChannelCondition(available); | 
| 206 |     return result; | 
| 207 | } | 
| 208 |  | 
| 209 | QList<QCanBusDeviceInfo> PeakCanBackend::interfaces() | 
| 210 | { | 
| 211 |     return attachedInterfaces(available: Availability::Available); | 
| 212 | } | 
| 213 |  | 
| 214 | QCanBusDeviceInfo PeakCanBackend::deviceInfo() const | 
| 215 | { | 
| 216 |     const uint index = d_ptr->channelIndex; | 
| 217 |     const QString name = pcanChannelNameForIndex(index); | 
| 218 |     const QList<QCanBusDeviceInfo> availableDevices = attachedInterfaces(available: Availability::Occupied); | 
| 219 |  | 
| 220 |     const auto deviceInfo = std::find_if(first: availableDevices.constBegin(), | 
| 221 |                                          last: availableDevices.constEnd(), | 
| 222 |                                          pred: [name](const QCanBusDeviceInfo &info) { | 
| 223 |         return name == info.name(); | 
| 224 |     }); | 
| 225 |  | 
| 226 |     if (Q_LIKELY(deviceInfo != availableDevices.constEnd())) | 
| 227 |         return *deviceInfo; | 
| 228 |  | 
| 229 |     qWarning(msg: "%s: Cannot get device info for index %u." , Q_FUNC_INFO, index); | 
| 230 |     return QCanBusDevice::deviceInfo(); | 
| 231 | } | 
| 232 |  | 
| 233 | #if defined(Q_OS_WIN32) | 
| 234 | class PeakCanReadNotifier : public QWinEventNotifier | 
| 235 | { | 
| 236 |     // no Q_OBJECT macro! | 
| 237 | public: | 
| 238 |     explicit PeakCanReadNotifier(PeakCanBackendPrivate *d, QObject *parent) | 
| 239 |         : QWinEventNotifier(parent) | 
| 240 |         , dptr(d) | 
| 241 |     { | 
| 242 |         setHandle(dptr->readHandle); | 
| 243 |  | 
| 244 |         connect(this, &QWinEventNotifier::activated, this, [this]() { | 
| 245 |             dptr->startRead(); | 
| 246 |         }); | 
| 247 |     } | 
| 248 |  | 
| 249 | private: | 
| 250 |     PeakCanBackendPrivate * const dptr; | 
| 251 | }; | 
| 252 | #else | 
| 253 | class PeakCanReadNotifier : public QSocketNotifier | 
| 254 | { | 
| 255 |     // no Q_OBJECT macro! | 
| 256 | public: | 
| 257 |     explicit PeakCanReadNotifier(PeakCanBackendPrivate *d, QObject *parent) | 
| 258 |         : QSocketNotifier(d->readHandle, QSocketNotifier::Read, parent) | 
| 259 |         , dptr(d) | 
| 260 |     { | 
| 261 |     } | 
| 262 |  | 
| 263 | protected: | 
| 264 |     bool event(QEvent *e) override | 
| 265 |     { | 
| 266 |         if (e->type() == QEvent::SockAct) { | 
| 267 |             dptr->startRead(); | 
| 268 |             return true; | 
| 269 |         } | 
| 270 |         return QSocketNotifier::event(e); | 
| 271 |     } | 
| 272 |  | 
| 273 | private: | 
| 274 |     PeakCanBackendPrivate * const dptr; | 
| 275 | }; | 
| 276 | #endif | 
| 277 |  | 
| 278 | class PeakCanWriteNotifier : public QTimer | 
| 279 | { | 
| 280 |     // no Q_OBJECT macro! | 
| 281 | public: | 
| 282 |     PeakCanWriteNotifier(PeakCanBackendPrivate *d, QObject *parent) | 
| 283 |         : QTimer(parent) | 
| 284 |         , dptr(d) | 
| 285 |     { | 
| 286 |     } | 
| 287 |  | 
| 288 | protected: | 
| 289 |     void timerEvent(QTimerEvent *e) override | 
| 290 |     { | 
| 291 |         if (e->timerId() == timerId()) { | 
| 292 |             dptr->startWrite(); | 
| 293 |             return; | 
| 294 |         } | 
| 295 |         QTimer::timerEvent(e); | 
| 296 |     } | 
| 297 |  | 
| 298 | private: | 
| 299 |     PeakCanBackendPrivate * const dptr; | 
| 300 | }; | 
| 301 |  | 
| 302 | PeakCanBackendPrivate::PeakCanBackendPrivate(PeakCanBackend *q) | 
| 303 |     : q_ptr(q) | 
| 304 | { | 
| 305 | } | 
| 306 |  | 
| 307 | struct BitrateItem | 
| 308 | { | 
| 309 |     int bitrate; | 
| 310 |     TPCANBaudrate code; | 
| 311 | }; | 
| 312 |  | 
| 313 | struct BitrateLessFunctor | 
| 314 | { | 
| 315 |     bool operator()( const BitrateItem &item1, const BitrateItem &item2) const | 
| 316 |     { | 
| 317 |         return item1.bitrate < item2.bitrate; | 
| 318 |     } | 
| 319 | }; | 
| 320 |  | 
| 321 | static TPCANBaudrate bitrateCodeFromBitrate(int bitrate) | 
| 322 | { | 
| 323 |     static const BitrateItem bitratetable[] = { | 
| 324 |         { .bitrate: 5000, PCAN_BAUD_5K }, | 
| 325 |         { .bitrate: 10000, PCAN_BAUD_10K }, | 
| 326 |         { .bitrate: 20000, PCAN_BAUD_20K }, | 
| 327 |         { .bitrate: 33000, PCAN_BAUD_33K }, | 
| 328 |         { .bitrate: 47000, PCAN_BAUD_47K }, | 
| 329 |         { .bitrate: 50000, PCAN_BAUD_50K }, | 
| 330 |         { .bitrate: 83000, PCAN_BAUD_83K }, | 
| 331 |         { .bitrate: 95000, PCAN_BAUD_95K }, | 
| 332 |         { .bitrate: 100000, PCAN_BAUD_100K }, | 
| 333 |         { .bitrate: 125000, PCAN_BAUD_125K }, | 
| 334 |         { .bitrate: 250000, PCAN_BAUD_250K }, | 
| 335 |         { .bitrate: 500000, PCAN_BAUD_500K }, | 
| 336 |         { .bitrate: 800000, PCAN_BAUD_800K }, | 
| 337 |         { .bitrate: 1000000, PCAN_BAUD_1M } | 
| 338 |     }; | 
| 339 |  | 
| 340 |     static const BitrateItem *endtable = bitratetable + (sizeof(bitratetable) / sizeof(*bitratetable)); | 
| 341 |  | 
| 342 |     const BitrateItem item = { .bitrate: bitrate , .code: 0 }; | 
| 343 |     const BitrateItem *where = std::lower_bound(first: bitratetable, last: endtable, val: item, comp: BitrateLessFunctor()); | 
| 344 |     return where != endtable ? where->code : PCAN_BAUD_INVALID; | 
| 345 | } | 
| 346 |  | 
| 347 | static QByteArray nominalBitrateString(int nominalBitrate) | 
| 348 | { | 
| 349 |     switch (nominalBitrate) { | 
| 350 |     case 125000: | 
| 351 |         return "f_clock=80000000, nom_brp=40, nom_tseg1=12, nom_tseg2=3, nom_sjw=1" ; | 
| 352 |     case 250000: | 
| 353 |         return "f_clock=80000000, nom_brp=20, nom_tseg1=12, nom_tseg2=3, nom_sjw=1" ; | 
| 354 |     case 500000: | 
| 355 |         return "f_clock=80000000, nom_brp=10, nom_tseg1=12, nom_tseg2=3, nom_sjw=1" ; | 
| 356 |     case 1000000: | 
| 357 |         return "f_clock=80000000, nom_brp=10, nom_tseg1=5,  nom_tseg2=2, nom_sjw=1" ; | 
| 358 |     default: | 
| 359 |         return QByteArray(); | 
| 360 |     } | 
| 361 | } | 
| 362 |  | 
| 363 | static QByteArray dataBitrateString(int dataBitrate) | 
| 364 | { | 
| 365 |     switch (dataBitrate) { | 
| 366 |     case 2000000: | 
| 367 |         return ", data_brp=4, data_tseg1=7, data_tseg2=2, data_sjw=1" ; | 
| 368 |     case 4000000: | 
| 369 |         return ", data_brp=2, data_tseg1=7, data_tseg2=2, data_sjw=1" ; | 
| 370 |     case 8000000: | 
| 371 |         return ", data_brp=1, data_tseg1=7, data_tseg2=2, data_sjw=1" ; | 
| 372 |     case 10000000: | 
| 373 |         return ", data_brp=1, data_tseg1=5, data_tseg2=2, data_sjw=1" ; | 
| 374 |     default: | 
| 375 |         return QByteArray(); | 
| 376 |     } | 
| 377 | } | 
| 378 |  | 
| 379 | static QByteArray bitrateStringFromBitrate(int nominalBitrate, int dataBitrate) | 
| 380 | { | 
| 381 |     QByteArray result = nominalBitrateString(nominalBitrate); | 
| 382 |  | 
| 383 |     if (result.isEmpty()) | 
| 384 |         return QByteArray(); | 
| 385 |  | 
| 386 |     result += dataBitrateString(dataBitrate); | 
| 387 |  | 
| 388 |     return result; | 
| 389 | } | 
| 390 |  | 
| 391 | bool PeakCanBackendPrivate::open() | 
| 392 | { | 
| 393 |     Q_Q(PeakCanBackend); | 
| 394 |  | 
| 395 |     const int nominalBitrate = q->configurationParameter(key: QCanBusDevice::BitRateKey).toInt(); | 
| 396 |     TPCANStatus st = PCAN_ERROR_OK; | 
| 397 |  | 
| 398 |     if (isFlexibleDatarateEnabled) { | 
| 399 |         const int dataBitrate = q->configurationParameter(key: QCanBusDevice::DataBitRateKey).toInt(); | 
| 400 |         const QByteArray bitrateStr = bitrateStringFromBitrate(nominalBitrate, dataBitrate); | 
| 401 |         st = ::CAN_InitializeFD(channelIndex, const_cast<char *>(bitrateStr.data())); | 
| 402 |     } else { | 
| 403 |         const TPCANBaudrate bitrateCode = bitrateCodeFromBitrate(bitrate: nominalBitrate); | 
| 404 |         st = ::CAN_Initialize(channelIndex, bitrateCode, 0, 0, 0); | 
| 405 |     } | 
| 406 |  | 
| 407 |     if (Q_UNLIKELY(st != PCAN_ERROR_OK)) { | 
| 408 |         const QString errorString = systemErrorString(errorCode: st); | 
| 409 |         qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot initialize hardware: %ls" , | 
| 410 |                    qUtf16Printable(errorString)); | 
| 411 |         q->setError(errorText: errorString, QCanBusDevice::ConnectionError); | 
| 412 |         return false; | 
| 413 |     } | 
| 414 |  | 
| 415 | #if defined(Q_OS_WIN32) | 
| 416 |     if (readHandle == INVALID_HANDLE_VALUE) { | 
| 417 |         readHandle = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); | 
| 418 |         if (Q_UNLIKELY(!readHandle)) { | 
| 419 |             const QString errorString = qt_error_string(int(::GetLastError())); | 
| 420 |             qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot create receive event handler: %ls" , | 
| 421 |                        qUtf16Printable(errorString)); | 
| 422 |             q->setError(errorString, QCanBusDevice::ConnectionError); | 
| 423 |             return false; | 
| 424 |         } | 
| 425 |     } | 
| 426 |  | 
| 427 |     const TPCANStatus err = ::CAN_SetValue(channelIndex, PCAN_RECEIVE_EVENT, &readHandle, sizeof(readHandle)); | 
| 428 |     if (Q_UNLIKELY(err != PCAN_ERROR_OK)) { | 
| 429 |         q->setError(systemErrorString(err), QCanBusDevice::ConnectionError); | 
| 430 |         return false; | 
| 431 |     } | 
| 432 |  | 
| 433 | #else | 
| 434 |     const TPCANStatus err = ::CAN_GetValue(channelIndex, PCAN_RECEIVE_EVENT, &readHandle, sizeof(readHandle)); | 
| 435 |     if (Q_UNLIKELY(err != PCAN_ERROR_OK)) { | 
| 436 |         const QString errorString = systemErrorString(errorCode: err); | 
| 437 |         qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot create receive event handler: %ls" , | 
| 438 |                    qUtf16Printable(errorString)); | 
| 439 |         q->setError(errorText: errorString, QCanBusDevice::ConnectionError); | 
| 440 |         return false; | 
| 441 |     } | 
| 442 | #endif | 
| 443 |  | 
| 444 |     writeNotifier = new PeakCanWriteNotifier(this, q); | 
| 445 |     writeNotifier->setInterval(0); | 
| 446 |  | 
| 447 |     readNotifier = new PeakCanReadNotifier(this, q); | 
| 448 |     readNotifier->setEnabled(true); | 
| 449 |  | 
| 450 |     isOpen = true; | 
| 451 |     return true; | 
| 452 | } | 
| 453 |  | 
| 454 | void PeakCanBackendPrivate::close() | 
| 455 | { | 
| 456 |     Q_Q(PeakCanBackend); | 
| 457 |  | 
| 458 |     delete readNotifier; | 
| 459 |     readNotifier = nullptr; | 
| 460 |  | 
| 461 |     delete writeNotifier; | 
| 462 |     writeNotifier = nullptr; | 
| 463 |  | 
| 464 |     quint32 value = 0; | 
| 465 |     const TPCANStatus err = ::CAN_SetValue(channelIndex, PCAN_RECEIVE_EVENT, &value, sizeof(value)); | 
| 466 |     if (Q_UNLIKELY(err != PCAN_ERROR_OK)) { | 
| 467 |         const QString errorString = systemErrorString(errorCode: err); | 
| 468 |         qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot unregister receive event handler: %ls" , | 
| 469 |                    qUtf16Printable(errorString)); | 
| 470 |         q->setError(errorText: errorString, QCanBusDevice::ConnectionError); | 
| 471 |     } | 
| 472 |  | 
| 473 |     const TPCANStatus st = ::CAN_Uninitialize(channelIndex); | 
| 474 |     if (Q_UNLIKELY(st != PCAN_ERROR_OK)) | 
| 475 |         q->setError(errorText: systemErrorString(errorCode: st), QCanBusDevice::ConnectionError); | 
| 476 |  | 
| 477 | #if defined(Q_OS_WIN32) | 
| 478 |     if (readHandle && (readHandle != INVALID_HANDLE_VALUE)) { | 
| 479 |         const QString errorString = qt_error_string(int(::GetLastError())); | 
| 480 |         if (Q_UNLIKELY(!::CloseHandle(readHandle))) { | 
| 481 |             qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot close read handle: %ls" , | 
| 482 |                        qUtf16Printable(errorString)); | 
| 483 |             q->setError(errorString, QCanBusDevice::ConnectionError); | 
| 484 |         } | 
| 485 |         readHandle = INVALID_HANDLE_VALUE; | 
| 486 |     } | 
| 487 | #else | 
| 488 |     readHandle = -1; | 
| 489 | #endif | 
| 490 |  | 
| 491 |     isOpen = false; | 
| 492 | } | 
| 493 |  | 
| 494 | bool PeakCanBackendPrivate::setConfigurationParameter(QCanBusDevice::ConfigurationKey key, | 
| 495 |                                                       const QVariant &value) | 
| 496 | { | 
| 497 |     Q_Q(PeakCanBackend); | 
| 498 |  | 
| 499 |     switch (key) { | 
| 500 |     case QCanBusDevice::BitRateKey: | 
| 501 |         return verifyBitRate(bitrate: value.toInt()); | 
| 502 |     case QCanBusDevice::CanFdKey: | 
| 503 |         isFlexibleDatarateEnabled = value.toBool(); | 
| 504 |         return true; | 
| 505 |     case QCanBusDevice::DataBitRateKey: { | 
| 506 |         const int dataBitrate = value.toInt(); | 
| 507 |         if (Q_UNLIKELY(dataBitrateString(dataBitrate).isEmpty())) { | 
| 508 |             qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Unsupported data bitrate value: %d" , dataBitrate); | 
| 509 |             q->setError(errorText: PeakCanBackend::tr(s: "Unsupported data bitrate value: %1." ).arg(a: dataBitrate), | 
| 510 |                         QCanBusDevice::ConfigurationError); | 
| 511 |             return false; | 
| 512 |         } | 
| 513 |         return true; | 
| 514 |     } | 
| 515 |     default: | 
| 516 |         qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Unsupported configuration key: %d" , key); | 
| 517 |         q->setError(errorText: PeakCanBackend::tr(s: "Unsupported configuration key: %1" ).arg(a: key), | 
| 518 |                     QCanBusDevice::ConfigurationError); | 
| 519 |         return false; | 
| 520 |     } | 
| 521 | } | 
| 522 |  | 
| 523 | void PeakCanBackendPrivate::setupChannel(const QByteArray &interfaceName) | 
| 524 | { | 
| 525 |     const PcanChannel *chn = pcanChannels; | 
| 526 |     while (chn->index != PCAN_NONEBUS && chn->name != interfaceName) | 
| 527 |         ++chn; | 
| 528 |     channelIndex = chn->index; | 
| 529 | } | 
| 530 |  | 
| 531 | // Calls only when the device is closed | 
| 532 | void PeakCanBackendPrivate::setupDefaultConfigurations() | 
| 533 | { | 
| 534 |     Q_Q(PeakCanBackend); | 
| 535 |  | 
| 536 |     q->setConfigurationParameter(key: QCanBusDevice::BitRateKey, value: 500000); | 
| 537 | } | 
| 538 |  | 
| 539 | QString PeakCanBackendPrivate::systemErrorString(TPCANStatus errorCode) | 
| 540 | { | 
| 541 |     QByteArray buffer(256, 0); | 
| 542 |     if (Q_UNLIKELY(::CAN_GetErrorText(errorCode, 0, buffer.data()) != PCAN_ERROR_OK)) | 
| 543 |         return PeakCanBackend::tr(s: "Unable to retrieve an error string" ); | 
| 544 |     return QString::fromLatin1(ba: buffer); | 
| 545 | } | 
| 546 |  | 
| 547 | enum CanFrameDlc { | 
| 548 |     Dlc00 =  0, | 
| 549 |     Dlc01 =  1, | 
| 550 |     Dlc02 =  2, | 
| 551 |     Dlc03 =  3, | 
| 552 |     Dlc04 =  4, | 
| 553 |     Dlc05 =  5, | 
| 554 |     Dlc06 =  6, | 
| 555 |     Dlc07 =  7, | 
| 556 |     Dlc08 =  8, | 
| 557 |     Dlc12 =  9, | 
| 558 |     Dlc16 = 10, | 
| 559 |     Dlc20 = 11, | 
| 560 |     Dlc24 = 12, | 
| 561 |     Dlc32 = 13, | 
| 562 |     Dlc48 = 14, | 
| 563 |     Dlc64 = 15 | 
| 564 | }; | 
| 565 |  | 
| 566 | static CanFrameDlc sizeToDlc(int size) | 
| 567 | { | 
| 568 |     switch (size) { | 
| 569 |     case 12: | 
| 570 |         return Dlc12; | 
| 571 |     case 16: | 
| 572 |         return Dlc16; | 
| 573 |     case 20: | 
| 574 |         return Dlc20; | 
| 575 |     case 24: | 
| 576 |         return Dlc24; | 
| 577 |     case 32: | 
| 578 |         return Dlc32; | 
| 579 |     case 48: | 
| 580 |         return Dlc48; | 
| 581 |     case 64: | 
| 582 |         return Dlc64; | 
| 583 |     default: | 
| 584 |         if (size >= 0 && size <= 8) | 
| 585 |             return static_cast<CanFrameDlc>(size); | 
| 586 |  | 
| 587 |         return Dlc00; | 
| 588 |     } | 
| 589 | } | 
| 590 |  | 
| 591 | static int dlcToSize(CanFrameDlc dlc) | 
| 592 | { | 
| 593 |     switch (dlc) { | 
| 594 |     case Dlc00: | 
| 595 |     case Dlc01: | 
| 596 |     case Dlc02: | 
| 597 |     case Dlc03: | 
| 598 |     case Dlc04: | 
| 599 |     case Dlc05: | 
| 600 |     case Dlc06: | 
| 601 |     case Dlc07: | 
| 602 |     case Dlc08: | 
| 603 |         return static_cast<int>(dlc); | 
| 604 |     case Dlc12: | 
| 605 |         return 12; | 
| 606 |     case Dlc16: | 
| 607 |         return 16; | 
| 608 |     case Dlc20: | 
| 609 |         return 20; | 
| 610 |     case Dlc24: | 
| 611 |         return 24; | 
| 612 |     case Dlc32: | 
| 613 |         return 32; | 
| 614 |     case Dlc48: | 
| 615 |         return 48; | 
| 616 |     case Dlc64: | 
| 617 |         return 64; | 
| 618 |     } | 
| 619 |     return 0; | 
| 620 | } | 
| 621 |  | 
| 622 | void PeakCanBackendPrivate::startWrite() | 
| 623 | { | 
| 624 |     Q_Q(PeakCanBackend); | 
| 625 |  | 
| 626 |     if (!q->hasOutgoingFrames()) { | 
| 627 |         writeNotifier->stop(); | 
| 628 |         return; | 
| 629 |     } | 
| 630 |  | 
| 631 |     const QCanBusFrame frame = q->dequeueOutgoingFrame(); | 
| 632 |     const QByteArray payload = frame.payload(); | 
| 633 |     const qsizetype payloadSize = payload.size(); | 
| 634 |     TPCANStatus st = PCAN_ERROR_OK; | 
| 635 |  | 
| 636 |     if (isFlexibleDatarateEnabled) { | 
| 637 |         TPCANMsgFD message = {}; | 
| 638 |         message.ID = frame.frameId(); | 
| 639 |         message.DLC = sizeToDlc(size: payloadSize); | 
| 640 |         message.MSGTYPE = frame.hasExtendedFrameFormat() ? PCAN_MESSAGE_EXTENDED | 
| 641 |                                                          : PCAN_MESSAGE_STANDARD; | 
| 642 |  | 
| 643 |         if (frame.hasFlexibleDataRateFormat()) | 
| 644 |             message.MSGTYPE |= PCAN_MESSAGE_FD; | 
| 645 |         if (frame.hasBitrateSwitch()) | 
| 646 |             message.MSGTYPE |= PCAN_MESSAGE_BRS; | 
| 647 |  | 
| 648 |         if (frame.frameType() == QCanBusFrame::RemoteRequestFrame) | 
| 649 |             message.MSGTYPE |= PCAN_MESSAGE_RTR; // we do not care about the payload | 
| 650 |         else | 
| 651 |             ::memcpy(dest: message.DATA, src: payload.constData(), n: payloadSize); | 
| 652 |         st = ::CAN_WriteFD(channelIndex, &message); | 
| 653 |     } else if (frame.hasFlexibleDataRateFormat()) { | 
| 654 |         const char errorString[] = "Cannot send CAN FD frame format as CAN FD is not enabled." ; | 
| 655 |         qCWarning(QT_CANBUS_PLUGINS_PEAKCAN(), errorString); | 
| 656 |         q->setError(errorText: PeakCanBackend::tr(s: errorString), QCanBusDevice::WriteError); | 
| 657 |     } else { | 
| 658 |         TPCANMsg message = {}; | 
| 659 |         message.ID = frame.frameId(); | 
| 660 |         message.LEN = static_cast<quint8>(payloadSize); | 
| 661 |         message.MSGTYPE = frame.hasExtendedFrameFormat() ? PCAN_MESSAGE_EXTENDED | 
| 662 |                                                          : PCAN_MESSAGE_STANDARD; | 
| 663 |  | 
| 664 |         if (frame.frameType() == QCanBusFrame::RemoteRequestFrame) | 
| 665 |             message.MSGTYPE |= PCAN_MESSAGE_RTR; // we do not care about the payload | 
| 666 |         else | 
| 667 |             ::memcpy(dest: message.DATA, src: payload.constData(), n: payloadSize); | 
| 668 |         st = ::CAN_Write(channelIndex, &message); | 
| 669 |     } | 
| 670 |  | 
| 671 |     if (Q_UNLIKELY(st != PCAN_ERROR_OK)) { | 
| 672 |         const QString errorString = systemErrorString(errorCode: st); | 
| 673 |         qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot write frame: %ls" , | 
| 674 |                   qUtf16Printable(errorString)); | 
| 675 |         q->setError(errorText: errorString, QCanBusDevice::WriteError); | 
| 676 |     } else { | 
| 677 |         emit q->framesWritten(framesCount: qint64(1)); | 
| 678 |     } | 
| 679 |  | 
| 680 |     if (q->hasOutgoingFrames() && !writeNotifier->isActive()) | 
| 681 |         writeNotifier->start(); | 
| 682 | } | 
| 683 |  | 
| 684 | void PeakCanBackendPrivate::startRead() | 
| 685 | { | 
| 686 |     Q_Q(PeakCanBackend); | 
| 687 |  | 
| 688 |     QList<QCanBusFrame> newFrames; | 
| 689 |  | 
| 690 |     for (;;) { | 
| 691 |         if (isFlexibleDatarateEnabled) { | 
| 692 |             TPCANMsgFD message = {}; | 
| 693 |             TPCANTimestampFD timestamp = {}; | 
| 694 |  | 
| 695 |             const TPCANStatus st = ::CAN_ReadFD(channelIndex, &message, ×tamp); | 
| 696 |             if (st != PCAN_ERROR_OK) { | 
| 697 |                 if (Q_UNLIKELY(st != PCAN_ERROR_QRCVEMPTY)) | 
| 698 |                     q->setError(errorText: systemErrorString(errorCode: st), QCanBusDevice::ReadError); | 
| 699 |                 break; | 
| 700 |             } | 
| 701 |  | 
| 702 |             // Filter out PCAN status frames, to avoid turning them | 
| 703 |             // into QCanBusFrame::DataFrames with random canId | 
| 704 |             if (Q_UNLIKELY(message.MSGTYPE & PCAN_MESSAGE_STATUS)) | 
| 705 |                 continue; | 
| 706 |  | 
| 707 |             const int size = dlcToSize(dlc: static_cast<CanFrameDlc>(message.DLC)); | 
| 708 |             QCanBusFrame frame(message.ID, QByteArray(reinterpret_cast<const char *>(message.DATA), size)); | 
| 709 |             frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(usec: static_cast<qint64>(timestamp))); | 
| 710 |             frame.setExtendedFrameFormat(message.MSGTYPE & PCAN_MESSAGE_EXTENDED); | 
| 711 |             frame.setFrameType((message.MSGTYPE & PCAN_MESSAGE_RTR) | 
| 712 |                                ? QCanBusFrame::RemoteRequestFrame : QCanBusFrame::DataFrame); | 
| 713 |             frame.setFlexibleDataRateFormat(message.MSGTYPE & PCAN_MESSAGE_FD); | 
| 714 |             frame.setBitrateSwitch(message.MSGTYPE & PCAN_MESSAGE_BRS); | 
| 715 |             frame.setErrorStateIndicator(message.MSGTYPE & PCAN_MESSAGE_ESI); | 
| 716 |  | 
| 717 |             newFrames.append(t: std::move(frame)); | 
| 718 |         } else { | 
| 719 |             TPCANMsg message = {}; | 
| 720 |             TPCANTimestamp timestamp = {}; | 
| 721 |  | 
| 722 |             const TPCANStatus st = ::CAN_Read(channelIndex, &message, ×tamp); | 
| 723 |             if (st != PCAN_ERROR_OK) { | 
| 724 |                 if (Q_UNLIKELY(st != PCAN_ERROR_QRCVEMPTY)) | 
| 725 |                     q->setError(errorText: systemErrorString(errorCode: st), QCanBusDevice::ReadError); | 
| 726 |                 break; | 
| 727 |             } | 
| 728 |  | 
| 729 |             // Filter out PCAN status frames, to avoid turning them | 
| 730 |             // into QCanBusFrame::DataFrames with random canId | 
| 731 |             if (Q_UNLIKELY(message.MSGTYPE & PCAN_MESSAGE_STATUS)) | 
| 732 |                 continue; | 
| 733 |  | 
| 734 |             const int size = static_cast<int>(message.LEN); | 
| 735 |             QCanBusFrame frame(message.ID, QByteArray(reinterpret_cast<const char *>(message.DATA), size)); | 
| 736 |             const quint64 millis = timestamp.millis + Q_UINT64_C(0x100000000) * timestamp.millis_overflow; | 
| 737 |             const quint64 micros = Q_UINT64_C(1000) * millis + timestamp.micros; | 
| 738 |             frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(usec: static_cast<qint64>(micros))); | 
| 739 |             frame.setExtendedFrameFormat(message.MSGTYPE & PCAN_MESSAGE_EXTENDED); | 
| 740 |             frame.setFrameType((message.MSGTYPE & PCAN_MESSAGE_RTR) | 
| 741 |                                ? QCanBusFrame::RemoteRequestFrame : QCanBusFrame::DataFrame); | 
| 742 |  | 
| 743 |             newFrames.append(t: std::move(frame)); | 
| 744 |         } | 
| 745 |     } | 
| 746 |  | 
| 747 |     q->enqueueReceivedFrames(newFrames); | 
| 748 | } | 
| 749 |  | 
| 750 | bool PeakCanBackendPrivate::verifyBitRate(int bitrate) | 
| 751 | { | 
| 752 |     Q_Q(PeakCanBackend); | 
| 753 |  | 
| 754 |     if (Q_UNLIKELY(isOpen)) { | 
| 755 |         const char errorString[] = "Cannot change bitrate for already opened device." ; | 
| 756 |         qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, errorString); | 
| 757 |         q->setError(errorText: PeakCanBackend::tr(s: errorString), QCanBusDevice::ConfigurationError); | 
| 758 |         return false; | 
| 759 |     } | 
| 760 |  | 
| 761 |     bool isValidBitrate = false; | 
| 762 |     if (q->configurationParameter(key: QCanBusDevice::CanFdKey).toBool()) | 
| 763 |         isValidBitrate = !nominalBitrateString(nominalBitrate: bitrate).isEmpty(); | 
| 764 |     else | 
| 765 |         isValidBitrate = bitrateCodeFromBitrate(bitrate) != PCAN_BAUD_INVALID; | 
| 766 |  | 
| 767 |     if (Q_UNLIKELY(!isValidBitrate)) { | 
| 768 |         qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Unsupported bitrate value: %d." , bitrate); | 
| 769 |         q->setError(errorText: PeakCanBackend::tr(s: "Unsupported bitrate value: %1." ).arg(a: bitrate), | 
| 770 |                     QCanBusDevice::ConfigurationError); | 
| 771 |     } | 
| 772 |  | 
| 773 |     return isValidBitrate; | 
| 774 | } | 
| 775 |  | 
| 776 | PeakCanBackend::PeakCanBackend(const QString &name, QObject *parent) | 
| 777 |     : QCanBusDevice(parent) | 
| 778 |     , d_ptr(new PeakCanBackendPrivate(this)) | 
| 779 | { | 
| 780 |     Q_D(PeakCanBackend); | 
| 781 |  | 
| 782 |     d->setupChannel(name.toLatin1()); | 
| 783 |     d->setupDefaultConfigurations(); | 
| 784 | } | 
| 785 |  | 
| 786 | PeakCanBackend::~PeakCanBackend() | 
| 787 | { | 
| 788 |     Q_D(PeakCanBackend); | 
| 789 |  | 
| 790 |     if (d->isOpen) | 
| 791 |         close(); | 
| 792 |  | 
| 793 |     delete d_ptr; | 
| 794 | } | 
| 795 |  | 
| 796 | bool PeakCanBackend::open() | 
| 797 | { | 
| 798 |     Q_D(PeakCanBackend); | 
| 799 |  | 
| 800 |     if (!d->isOpen) { | 
| 801 |         if (Q_UNLIKELY(!d->open())) | 
| 802 |             return false; | 
| 803 |  | 
| 804 |         // Apply all stored configurations except bitrate, because | 
| 805 |         // the bitrate cannot be changed after opening the device | 
| 806 |         const auto keys = configurationKeys(); | 
| 807 |         for (ConfigurationKey key : keys) { | 
| 808 |             if (key == QCanBusDevice::BitRateKey || key == QCanBusDevice::DataBitRateKey) | 
| 809 |                 continue; | 
| 810 |             const QVariant param = configurationParameter(key); | 
| 811 |             const bool success = d->setConfigurationParameter(key, value: param); | 
| 812 |             if (Q_UNLIKELY(!success)) { | 
| 813 |                 qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot apply parameter: %d with value: %ls." , | 
| 814 |                           key, qUtf16Printable(param.toString())); | 
| 815 |             } | 
| 816 |         } | 
| 817 |     } | 
| 818 |  | 
| 819 |     setState(QCanBusDevice::ConnectedState); | 
| 820 |     return true; | 
| 821 | } | 
| 822 |  | 
| 823 | void PeakCanBackend::close() | 
| 824 | { | 
| 825 |     Q_D(PeakCanBackend); | 
| 826 |  | 
| 827 |     d->close(); | 
| 828 |  | 
| 829 |     setState(QCanBusDevice::UnconnectedState); | 
| 830 | } | 
| 831 |  | 
| 832 | void PeakCanBackend::setConfigurationParameter(ConfigurationKey key, const QVariant &value) | 
| 833 | { | 
| 834 |     Q_D(PeakCanBackend); | 
| 835 |  | 
| 836 |     if (d->setConfigurationParameter(key, value)) | 
| 837 |         QCanBusDevice::setConfigurationParameter(key, value); | 
| 838 | } | 
| 839 |  | 
| 840 | bool PeakCanBackend::writeFrame(const QCanBusFrame &newData) | 
| 841 | { | 
| 842 |     Q_D(PeakCanBackend); | 
| 843 |  | 
| 844 |     if (Q_UNLIKELY(state() != QCanBusDevice::ConnectedState)) | 
| 845 |         return false; | 
| 846 |  | 
| 847 |     if (Q_UNLIKELY(!newData.isValid())) { | 
| 848 |         setError(errorText: tr(s: "Cannot write invalid QCanBusFrame" ), QCanBusDevice::WriteError); | 
| 849 |         return false; | 
| 850 |     } | 
| 851 |  | 
| 852 |     if (Q_UNLIKELY(newData.frameType() != QCanBusFrame::DataFrame | 
| 853 |                    && newData.frameType() != QCanBusFrame::RemoteRequestFrame)) { | 
| 854 |         setError(errorText: tr(s: "Unable to write a frame with unacceptable type" ), | 
| 855 |                  QCanBusDevice::WriteError); | 
| 856 |         return false; | 
| 857 |     } | 
| 858 |  | 
| 859 |     enqueueOutgoingFrame(newFrame: newData); | 
| 860 |  | 
| 861 |     if (!d->writeNotifier->isActive()) | 
| 862 |         d->writeNotifier->start(); | 
| 863 |  | 
| 864 |     return true; | 
| 865 | } | 
| 866 |  | 
| 867 | // TODO: Implement me | 
| 868 | QString PeakCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame) | 
| 869 | { | 
| 870 |     Q_UNUSED(errorFrame); | 
| 871 |  | 
| 872 |     return QString(); | 
| 873 | } | 
| 874 |  | 
| 875 | void PeakCanBackend::resetController() | 
| 876 | { | 
| 877 |     close(); | 
| 878 |     open(); | 
| 879 | } | 
| 880 |  | 
| 881 | bool PeakCanBackend::hasBusStatus() const | 
| 882 | { | 
| 883 |     return true; | 
| 884 | } | 
| 885 |  | 
| 886 | QCanBusDevice::CanBusStatus PeakCanBackend::busStatus() | 
| 887 | { | 
| 888 |     const TPCANStatus status = ::CAN_GetStatus(d_ptr->channelIndex); | 
| 889 |  | 
| 890 |     switch (status & PCAN_ERROR_ANYBUSERR) { | 
| 891 |     case PCAN_ERROR_OK: | 
| 892 |         return QCanBusDevice::CanBusStatus::Good; | 
| 893 |     case PCAN_ERROR_BUSWARNING: | 
| 894 |         return QCanBusDevice::CanBusStatus::Warning; | 
| 895 |     case PCAN_ERROR_BUSPASSIVE: | 
| 896 |         return QCanBusDevice::CanBusStatus::Error; | 
| 897 |     case PCAN_ERROR_BUSOFF: | 
| 898 |         return QCanBusDevice::CanBusStatus::BusOff; | 
| 899 |     default: | 
| 900 |         qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Unknown CAN bus status: %lu." , ulong(status)); | 
| 901 |         return QCanBusDevice::CanBusStatus::Unknown; | 
| 902 |     } | 
| 903 | } | 
| 904 |  | 
| 905 | QT_END_NAMESPACE | 
| 906 |  |