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
25QT_BEGIN_NAMESPACE
26
27Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_PEAKCAN)
28
29#ifndef LINK_LIBPCANBASIC
30Q_GLOBAL_STATIC(QLibrary, pcanLibrary)
31#endif
32
33bool 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
58struct PcanChannel{
59 char name[6];
60 TPCANHandle index;
61};
62static 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
98QList<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
141static 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
154QList<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
196QList<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
209QList<QCanBusDeviceInfo> PeakCanBackend::interfaces()
210{
211 return attachedInterfaces(available: Availability::Available);
212}
213
214QCanBusDeviceInfo 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)
234class PeakCanReadNotifier : public QWinEventNotifier
235{
236 // no Q_OBJECT macro!
237public:
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
249private:
250 PeakCanBackendPrivate * const dptr;
251};
252#else
253class PeakCanReadNotifier : public QSocketNotifier
254{
255 // no Q_OBJECT macro!
256public:
257 explicit PeakCanReadNotifier(PeakCanBackendPrivate *d, QObject *parent)
258 : QSocketNotifier(d->readHandle, QSocketNotifier::Read, parent)
259 , dptr(d)
260 {
261 }
262
263protected:
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
273private:
274 PeakCanBackendPrivate * const dptr;
275};
276#endif
277
278class PeakCanWriteNotifier : public QTimer
279{
280 // no Q_OBJECT macro!
281public:
282 PeakCanWriteNotifier(PeakCanBackendPrivate *d, QObject *parent)
283 : QTimer(parent)
284 , dptr(d)
285 {
286 }
287
288protected:
289 void timerEvent(QTimerEvent *e) override
290 {
291 if (e->timerId() == timerId()) {
292 dptr->startWrite();
293 return;
294 }
295 QTimer::timerEvent(e);
296 }
297
298private:
299 PeakCanBackendPrivate * const dptr;
300};
301
302PeakCanBackendPrivate::PeakCanBackendPrivate(PeakCanBackend *q)
303 : q_ptr(q)
304{
305}
306
307struct BitrateItem
308{
309 int bitrate;
310 TPCANBaudrate code;
311};
312
313struct BitrateLessFunctor
314{
315 bool operator()( const BitrateItem &item1, const BitrateItem &item2) const
316 {
317 return item1.bitrate < item2.bitrate;
318 }
319};
320
321static 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
347static 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
363static 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
379static 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
391bool 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
454void 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
494bool 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
523void 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
532void PeakCanBackendPrivate::setupDefaultConfigurations()
533{
534 Q_Q(PeakCanBackend);
535
536 q->setConfigurationParameter(key: QCanBusDevice::BitRateKey, value: 500000);
537}
538
539QString 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
547enum 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
566static 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
591static 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
622void 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
684void 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, &timestamp);
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, &timestamp);
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
750bool 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
776PeakCanBackend::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
786PeakCanBackend::~PeakCanBackend()
787{
788 Q_D(PeakCanBackend);
789
790 if (d->isOpen)
791 close();
792
793 delete d_ptr;
794}
795
796bool 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
823void PeakCanBackend::close()
824{
825 Q_D(PeakCanBackend);
826
827 d->close();
828
829 setState(QCanBusDevice::UnconnectedState);
830}
831
832void 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
840bool 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
868QString PeakCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame)
869{
870 Q_UNUSED(errorFrame);
871
872 return QString();
873}
874
875void PeakCanBackend::resetController()
876{
877 close();
878 open();
879}
880
881bool PeakCanBackend::hasBusStatus() const
882{
883 return true;
884}
885
886QCanBusDevice::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
905QT_END_NAMESPACE
906

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