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

source code of qtserialbus/src/plugins/canbus/peakcan/peakcanbackend.cpp