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