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 | |