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