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

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