1// Copyright (C) 2017 Denis Shienkov <denis.shienkov@gmail.com>
2// Copyright (C) 2017 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:critical reason:data-parser
5
6#include "tinycanbackend.h"
7#include "tinycanbackend_p.h"
8
9#include "tinycan_symbols_p.h"
10
11#include <QtSerialBus/qcanbusdevice.h>
12
13#include <QtCore/qtimer.h>
14#include <QtCore/qmutex.h>
15#include <QtCore/qcoreevent.h>
16#include <QtCore/qloggingcategory.h>
17
18#include <algorithm>
19
20QT_BEGIN_NAMESPACE
21
22Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_TINYCAN)
23
24#ifndef LINK_LIBMHSTCAN
25Q_GLOBAL_STATIC(QLibrary, mhstcanLibrary)
26#endif
27
28bool TinyCanBackend::canCreate(QString *errorReason)
29{
30#ifdef LINK_LIBMHSTCAN
31 return true;
32#else
33 static bool symbolsResolved = resolveTinyCanSymbols(mhstcanLibrary: mhstcanLibrary());
34 if (Q_UNLIKELY(!symbolsResolved)) {
35 *errorReason = mhstcanLibrary()->errorString();
36 return false;
37 }
38 return true;
39#endif
40}
41
42QList<QCanBusDeviceInfo> TinyCanBackend::interfaces()
43{
44 QList<QCanBusDeviceInfo> result;
45 result.append(t: createDeviceInfo(QStringLiteral("tinycan"), QStringLiteral("can0.0"),
46 isVirtual: false, isFlexibleDataRateCapable: false));
47 return result;
48}
49
50namespace {
51
52struct TinyCanGlobal {
53 QList<TinyCanBackendPrivate *> channels;
54 QMutex mutex;
55};
56
57} // namespace
58
59Q_GLOBAL_STATIC(TinyCanGlobal, gTinyCan)
60
61class TinyCanWriteNotifier : public QTimer
62{
63 // no Q_OBJECT macro!
64public:
65 TinyCanWriteNotifier(TinyCanBackendPrivate *d, QObject *parent)
66 : QTimer(parent)
67 , dptr(d)
68 {
69 }
70
71protected:
72 void timerEvent(QTimerEvent *e) override
73 {
74 if (e->timerId() == timerId()) {
75 dptr->startWrite();
76 return;
77 }
78 QTimer::timerEvent(e);
79 }
80
81private:
82 TinyCanBackendPrivate * const dptr;
83};
84
85static int driverRefCount = 0;
86
87static void DRV_CALLBACK_TYPE canRxEventCallback(quint32 index, TCanMsg *frame, qint32 count)
88{
89 Q_UNUSED(frame);
90 Q_UNUSED(count);
91
92 QMutexLocker lock(&gTinyCan->mutex);
93 for (TinyCanBackendPrivate *p : std::as_const(t&: gTinyCan->channels)) {
94 if (p->channelIndex == int(index)) {
95 p->startRead();
96 return;
97 }
98 }
99}
100
101TinyCanBackendPrivate::TinyCanBackendPrivate(TinyCanBackend *q)
102 : q_ptr(q)
103{
104 startupDriver();
105
106 QMutexLocker lock(&gTinyCan->mutex);
107 gTinyCan->channels.append(t: this);
108}
109
110TinyCanBackendPrivate::~TinyCanBackendPrivate()
111{
112 cleanupDriver();
113
114 QMutexLocker lock(&gTinyCan->mutex);
115 gTinyCan->channels.removeAll(t: this);
116}
117
118struct BitrateItem
119{
120 int bitrate;
121 int code;
122};
123
124struct BitrateLessFunctor
125{
126 bool operator()( const BitrateItem &item1, const BitrateItem &item2) const
127 {
128 return item1.bitrate < item2.bitrate;
129 }
130};
131
132static int bitrateCodeFromBitrate(int bitrate)
133{
134 static const BitrateItem bitratetable[] = {
135 { .bitrate: 10000, CAN_10K_BIT },
136 { .bitrate: 20000, CAN_20K_BIT },
137 { .bitrate: 50000, CAN_50K_BIT },
138 { .bitrate: 100000, CAN_100K_BIT },
139 { .bitrate: 125000, CAN_125K_BIT },
140 { .bitrate: 250000, CAN_250K_BIT },
141 { .bitrate: 500000, CAN_500K_BIT },
142 { .bitrate: 800000, CAN_800K_BIT },
143 { .bitrate: 1000000, CAN_1M_BIT }
144 };
145
146 static const BitrateItem *endtable = bitratetable + (sizeof(bitratetable) / sizeof(*bitratetable));
147
148 const BitrateItem item = { .bitrate: bitrate , .code: 0 };
149 const BitrateItem *where = std::lower_bound(first: bitratetable, last: endtable, val: item, comp: BitrateLessFunctor());
150 return where != endtable ? where->code : -1;
151}
152
153bool TinyCanBackendPrivate::open()
154{
155 Q_Q(TinyCanBackend);
156
157 {
158 char options[] = "AutoConnect=1;AutoReopen=0";
159 const int ret = ::CanSetOptions(options);
160 if (Q_UNLIKELY(ret < 0)) {
161 q->setError(errorText: systemErrorString(errorCode: ret), QCanBusDevice::CanBusError::ConnectionError);
162 return false;
163 }
164 }
165
166 {
167 const int ret = ::CanDeviceOpen(channelIndex, nullptr);
168 if (Q_UNLIKELY(ret < 0)) {
169 q->setError(errorText: systemErrorString(errorCode: ret), QCanBusDevice::CanBusError::ConnectionError);
170 return false;
171 }
172 }
173
174 {
175 const int ret = ::CanSetMode(channelIndex, OP_CAN_START, CAN_CMD_ALL_CLEAR);
176 if (Q_UNLIKELY(ret < 0)) {
177 q->setError(errorText: systemErrorString(errorCode: ret), QCanBusDevice::CanBusError::ConnectionError);
178 ::CanDeviceClose(channelIndex);
179 return false;
180 }
181 }
182
183 writeNotifier = new TinyCanWriteNotifier(this, q);
184 writeNotifier->setInterval(0);
185
186 isOpen = true;
187 return true;
188}
189
190void TinyCanBackendPrivate::close()
191{
192 Q_Q(TinyCanBackend);
193
194 delete writeNotifier;
195 writeNotifier = nullptr;
196
197 const int ret = ::CanDeviceClose(channelIndex);
198 if (Q_UNLIKELY(ret < 0))
199 q->setError(errorText: systemErrorString(errorCode: ret), QCanBusDevice::CanBusError::ConnectionError);
200
201 isOpen = false;
202}
203
204bool TinyCanBackendPrivate::setConfigurationParameter(QCanBusDevice::ConfigurationKey key,
205 const QVariant &value)
206{
207 Q_Q(TinyCanBackend);
208
209 switch (key) {
210 case QCanBusDevice::BitRateKey:
211 return setBitRate(value.toInt());
212 default:
213 q->setError(errorText: TinyCanBackend::tr(s: "Unsupported configuration key: %1").arg(a: key),
214 QCanBusDevice::ConfigurationError);
215 return false;
216 }
217}
218
219// These error codes taked from the errors.h file, which
220// exists only in linux sources.
221QString TinyCanBackendPrivate::systemErrorString(int errorCode)
222{
223 switch (errorCode) {
224 case 0:
225 return TinyCanBackend::tr(s: "No error");
226 case -1:
227 return TinyCanBackend::tr(s: "Driver not initialized");
228 case -2:
229 return TinyCanBackend::tr(s: "Invalid parameters values were passed");
230 case -3:
231 return TinyCanBackend::tr(s: "Invalid index value");
232 case -4:
233 return TinyCanBackend::tr(s: "More invalid CAN-channel");
234 case -5:
235 return TinyCanBackend::tr(s: "General error");
236 case -6:
237 return TinyCanBackend::tr(s: "The FIFO cannot be written");
238 case -7:
239 return TinyCanBackend::tr(s: "The buffer cannot be written");
240 case -8:
241 return TinyCanBackend::tr(s: "The FIFO cannot be read");
242 case -9:
243 return TinyCanBackend::tr(s: "The buffer cannot be read");
244 case -10:
245 return TinyCanBackend::tr(s: "Variable not found");
246 case -11:
247 return TinyCanBackend::tr(s: "Reading of the variable does not permit");
248 case -12:
249 return TinyCanBackend::tr(s: "Reading buffer for variable too small");
250 case -13:
251 return TinyCanBackend::tr(s: "Writing of the variable does not permit");
252 case -14:
253 return TinyCanBackend::tr(s: "The string/stream to be written is to majority");
254 case -15:
255 return TinyCanBackend::tr(s: "Fell short min of value");
256 case -16:
257 return TinyCanBackend::tr(s: "Max value crossed");
258 case -17:
259 return TinyCanBackend::tr(s: "Access refuses");
260 case -18:
261 return TinyCanBackend::tr(s: "Invalid value of CAN speed");
262 case -19:
263 return TinyCanBackend::tr(s: "Invalid value of baud rate");
264 case -20:
265 return TinyCanBackend::tr(s: "Value not put");
266 case -21:
267 return TinyCanBackend::tr(s: "No connection to the hardware");
268 case -22:
269 return TinyCanBackend::tr(s: "Communication error to the hardware");
270 case -23:
271 return TinyCanBackend::tr(s: "Hardware sends wrong number of parameters");
272 case -24:
273 return TinyCanBackend::tr(s: "Not enough main memory");
274 case -25:
275 return TinyCanBackend::tr(s: "The system cannot provide the enough resources");
276 case -26:
277 return TinyCanBackend::tr(s: "A system call returns with an error");
278 case -27:
279 return TinyCanBackend::tr(s: "The main thread is occupied");
280 case -28:
281 return TinyCanBackend::tr(s: "User allocated memory not found");
282 case -29:
283 return TinyCanBackend::tr(s: "The main thread cannot be launched");
284 // the codes -30...-33 are skipped, as they belongs to sockets
285 default:
286 return TinyCanBackend::tr(s: "Unknown error");
287 }
288}
289
290static int channelIndexFromName(const QString &interfaceName)
291{
292 if (interfaceName == QStringLiteral("can0.0"))
293 return INDEX_CAN_KANAL_A;
294 else if (interfaceName == QStringLiteral("can0.1"))
295 return INDEX_CAN_KANAL_B;
296 else
297 return INDEX_INVALID;
298}
299
300void TinyCanBackendPrivate::setupChannel(const QString &interfaceName)
301{
302 channelIndex = channelIndexFromName(interfaceName);
303}
304
305// Calls only in constructor
306void TinyCanBackendPrivate::setupDefaultConfigurations()
307{
308 Q_Q(TinyCanBackend);
309
310 q->setConfigurationParameter(key: QCanBusDevice::BitRateKey, value: 500000);
311}
312
313void TinyCanBackendPrivate::startWrite()
314{
315 Q_Q(TinyCanBackend);
316
317 if (!q->hasOutgoingFrames()) {
318 writeNotifier->stop();
319 return;
320 }
321
322 const QCanBusFrame frame = q->dequeueOutgoingFrame();
323 const QByteArray payload = frame.payload();
324 const qsizetype payloadSize = payload.size();
325
326 TCanMsg message = {};
327 message.Id = frame.frameId();
328 message.Flags.Flag.Len = payloadSize;
329 message.Flags.Flag.Error = (frame.frameType() == QCanBusFrame::ErrorFrame);
330 message.Flags.Flag.RTR = (frame.frameType() == QCanBusFrame::RemoteRequestFrame);
331 message.Flags.Flag.TxD = 1;
332 message.Flags.Flag.EFF = frame.hasExtendedFrameFormat();
333
334 const qint32 messagesToWrite = 1;
335 ::memcpy(dest: message.Data.Bytes, src: payload.constData(), n: payloadSize);
336 const int ret = ::CanTransmit(channelIndex, &message, messagesToWrite);
337 if (Q_UNLIKELY(ret < 0))
338 q->setError(errorText: systemErrorString(errorCode: ret), QCanBusDevice::CanBusError::WriteError);
339 else
340 emit q->framesWritten(framesCount: messagesToWrite);
341
342 if (q->hasOutgoingFrames() && !writeNotifier->isActive())
343 writeNotifier->start();
344}
345
346// this method is called from the different thread!
347void TinyCanBackendPrivate::startRead()
348{
349 Q_Q(TinyCanBackend);
350
351 QList<QCanBusFrame> newFrames;
352
353 for (;;) {
354 if (!::CanReceiveGetCount(channelIndex))
355 break;
356
357 TCanMsg message = {};
358
359 const int messagesToRead = 1;
360 const int ret = ::CanReceive(channelIndex, &message, messagesToRead);
361 if (Q_UNLIKELY(ret < 0)) {
362 q->setError(errorText: systemErrorString(errorCode: ret), QCanBusDevice::CanBusError::ReadError);
363
364 TDeviceStatus status = {};
365
366 if (::CanGetDeviceStatus(channelIndex, &status) < 0) {
367 q->setError(errorText: systemErrorString(errorCode: ret), QCanBusDevice::CanBusError::ReadError);
368 } else {
369 if (status.CanStatus == CAN_STATUS_BUS_OFF) {
370 qCWarning(QT_CANBUS_PLUGINS_TINYCAN, "CAN bus is in off state, trying to reset the bus.");
371 resetController();
372 }
373 }
374
375 continue;
376 }
377
378 QCanBusFrame frame(message.Id, QByteArray(reinterpret_cast<char *>(message.Data.Bytes),
379 int(message.Flags.Flag.Len)));
380 frame.setTimeStamp(QCanBusFrame::TimeStamp(message.Time.Sec, message.Time.USec));
381 frame.setExtendedFrameFormat(message.Flags.Flag.EFF);
382
383 if (message.Flags.Flag.Error)
384 frame.setFrameType(QCanBusFrame::ErrorFrame);
385 else if (message.Flags.Flag.RTR)
386 frame.setFrameType(QCanBusFrame::RemoteRequestFrame);
387 else
388 frame.setFrameType(QCanBusFrame::DataFrame);
389
390 newFrames.append(t: std::move(frame));
391 }
392
393 q->enqueueReceivedFrames(newFrames);
394}
395
396void TinyCanBackendPrivate::startupDriver()
397{
398 Q_Q(TinyCanBackend);
399
400 if (driverRefCount == 0) {
401 const int ret = ::CanInitDriver(nullptr);
402 if (Q_UNLIKELY(ret < 0)) {
403 q->setError(errorText: systemErrorString(errorCode: ret), QCanBusDevice::CanBusError::ConnectionError);
404 return;
405 }
406
407 ::CanSetRxEventCallback(&canRxEventCallback);
408 ::CanSetEvents(EVENT_ENABLE_RX_MESSAGES);
409
410 } else if (Q_UNLIKELY(driverRefCount < 0)) {
411 qCCritical(QT_CANBUS_PLUGINS_TINYCAN, "Wrong driver reference counter: %d",
412 driverRefCount);
413 return;
414 }
415
416 ++driverRefCount;
417}
418
419void TinyCanBackendPrivate::cleanupDriver()
420{
421 --driverRefCount;
422
423 if (Q_UNLIKELY(driverRefCount < 0)) {
424 qCCritical(QT_CANBUS_PLUGINS_TINYCAN, "Wrong driver reference counter: %d",
425 driverRefCount);
426 driverRefCount = 0;
427 } else if (driverRefCount == 0) {
428 ::CanSetEvents(EVENT_DISABLE_ALL);
429 ::CanDownDriver();
430 }
431}
432
433void TinyCanBackendPrivate::resetController()
434{
435 Q_Q(TinyCanBackend);
436 qint32 ret = ::CanSetMode(channelIndex, OP_CAN_RESET, CAN_CMD_NONE);
437 if (Q_UNLIKELY(ret < 0)) {
438 const QString errorString = systemErrorString(errorCode: ret);
439 qCWarning(QT_CANBUS_PLUGINS_TINYCAN, "Cannot perform hardware reset: %ls",
440 qUtf16Printable(errorString));
441 q->setError(errorText: errorString, QCanBusDevice::CanBusError::ConfigurationError);
442 }
443}
444
445bool TinyCanBackendPrivate::setBitRate(int bitrate)
446{
447 Q_Q(TinyCanBackend);
448
449 const int bitrateCode = bitrateCodeFromBitrate(bitrate);
450 if (Q_UNLIKELY(bitrateCode == -1)) {
451 q->setError(errorText: TinyCanBackend::tr(s: "Unsupported bitrate value"),
452 QCanBusDevice::ConfigurationError);
453 return false;
454 }
455
456 if (isOpen) {
457 const int ret = ::CanSetSpeed(channelIndex, bitrateCode);
458 if (Q_UNLIKELY(ret < 0)) {
459 q->setError(errorText: systemErrorString(errorCode: ret), QCanBusDevice::CanBusError::ConfigurationError);
460 return false;
461 }
462 }
463
464 return true;
465}
466
467TinyCanBackend::TinyCanBackend(const QString &name, QObject *parent)
468 : QCanBusDevice(parent)
469 , d_ptr(new TinyCanBackendPrivate(this))
470{
471 Q_D(TinyCanBackend);
472
473 d->setupChannel(name);
474 d->setupDefaultConfigurations();
475}
476
477TinyCanBackend::~TinyCanBackend()
478{
479 close();
480 delete d_ptr;
481}
482
483bool TinyCanBackend::open()
484{
485 Q_D(TinyCanBackend);
486
487 if (!d->isOpen) {
488 if (!d->open()) {
489 close(); // sets UnconnectedState
490 return false;
491 }
492
493 // apply all stored configurations
494 const auto keys = configurationKeys();
495 for (ConfigurationKey key : keys) {
496 const QVariant param = configurationParameter(key);
497 const bool success = d->setConfigurationParameter(key, value: param);
498 if (Q_UNLIKELY(!success)) {
499 qCWarning(QT_CANBUS_PLUGINS_TINYCAN, "Cannot apply parameter: %d with value: %ls.",
500 key, qUtf16Printable(param.toString()));
501 }
502 }
503 }
504
505 setState(QCanBusDevice::ConnectedState);
506 return true;
507}
508
509void TinyCanBackend::close()
510{
511 Q_D(TinyCanBackend);
512
513 d->close();
514
515 setState(QCanBusDevice::UnconnectedState);
516}
517
518void TinyCanBackend::setConfigurationParameter(ConfigurationKey key, const QVariant &value)
519{
520 Q_D(TinyCanBackend);
521
522 if (d->setConfigurationParameter(key, value))
523 QCanBusDevice::setConfigurationParameter(key, value);
524}
525
526bool TinyCanBackend::writeFrame(const QCanBusFrame &newData)
527{
528 Q_D(TinyCanBackend);
529
530 if (Q_UNLIKELY(state() != QCanBusDevice::ConnectedState))
531 return false;
532
533 if (Q_UNLIKELY(!newData.isValid())) {
534 setError(errorText: tr(s: "Cannot write invalid QCanBusFrame"), QCanBusDevice::WriteError);
535 return false;
536 }
537
538 if (Q_UNLIKELY(newData.frameType() != QCanBusFrame::DataFrame
539 && newData.frameType() != QCanBusFrame::RemoteRequestFrame
540 && newData.frameType() != QCanBusFrame::ErrorFrame)) {
541 setError(errorText: tr(s: "Unable to write a frame with unacceptable type"),
542 QCanBusDevice::WriteError);
543 return false;
544 }
545
546 // CAN FD frame format not supported at this stage
547 if (Q_UNLIKELY(newData.hasFlexibleDataRateFormat())) {
548 setError(errorText: tr(s: "CAN FD frame format not supported."), QCanBusDevice::WriteError);
549 return false;
550 }
551
552 enqueueOutgoingFrame(newFrame: newData);
553
554 if (!d->writeNotifier->isActive())
555 d->writeNotifier->start();
556
557 return true;
558}
559
560// TODO: Implement me
561QString TinyCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame)
562{
563 Q_UNUSED(errorFrame);
564
565 return QString();
566}
567
568void TinyCanBackend::resetController()
569{
570 Q_D(TinyCanBackend);
571 d->resetController();
572}
573
574QCanBusDeviceInfo TinyCanBackend::deviceInfo() const
575{
576 return createDeviceInfo(QStringLiteral("tinycan"), QStringLiteral("can0.0"), isVirtual: false, isFlexibleDataRateCapable: false);
577}
578
579QT_END_NAMESPACE
580

source code of qtserialbus/src/plugins/canbus/tinycan/tinycanbackend.cpp