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 
52 QT_BEGIN_NAMESPACE
53 
Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_TINYCAN)54 Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_TINYCAN)
55 
56 #ifndef LINK_LIBMHSTCAN
57 Q_GLOBAL_STATIC(QLibrary, mhstcanLibrary)
58 #endif
59 
60 bool TinyCanBackend::canCreate(QString *errorReason)
61 {
62 #ifdef LINK_LIBMHSTCAN
63     return true;
64 #else
65     static bool symbolsResolved = resolveTinyCanSymbols(mhstcanLibrary());
66     if (Q_UNLIKELY(!symbolsResolved)) {
67         *errorReason = mhstcanLibrary()->errorString();
68         return false;
69     }
70     return true;
71 #endif
72 }
73 
interfaces()74 QList<QCanBusDeviceInfo> TinyCanBackend::interfaces()
75 {
76     QList<QCanBusDeviceInfo> result;
77     result.append(createDeviceInfo(QStringLiteral("can0.0")));
78     return result;
79 }
80 
81 namespace {
82 
83 struct TinyCanGlobal {
84     QList<TinyCanBackendPrivate *> channels;
85     QMutex mutex;
86 };
87 
88 } // namespace
89 
90 Q_GLOBAL_STATIC(TinyCanGlobal, gTinyCan)
91 
92 class TinyCanWriteNotifier : public QTimer
93 {
94     // no Q_OBJECT macro!
95 public:
TinyCanWriteNotifier(TinyCanBackendPrivate * d,QObject * parent)96     TinyCanWriteNotifier(TinyCanBackendPrivate *d, QObject *parent)
97         : QTimer(parent)
98         , dptr(d)
99     {
100     }
101 
102 protected:
timerEvent(QTimerEvent * e)103     void timerEvent(QTimerEvent *e) override
104     {
105         if (e->timerId() == timerId()) {
106             dptr->startWrite();
107             return;
108         }
109         QTimer::timerEvent(e);
110     }
111 
112 private:
113     TinyCanBackendPrivate * const dptr;
114 };
115 
116 static int driverRefCount = 0;
117 
canRxEventCallback(quint32 index,TCanMsg * frame,qint32 count)118 static 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(gTinyCan->channels)) {
125         if (p->channelIndex == int(index)) {
126             p->startRead();
127             return;
128         }
129     }
130 }
131 
TinyCanBackendPrivate(TinyCanBackend * q)132 TinyCanBackendPrivate::TinyCanBackendPrivate(TinyCanBackend *q)
133     : q_ptr(q)
134 {
135     startupDriver();
136 
137     QMutexLocker lock(&gTinyCan->mutex);
138     gTinyCan->channels.append(this);
139 }
140 
~TinyCanBackendPrivate()141 TinyCanBackendPrivate::~TinyCanBackendPrivate()
142 {
143     cleanupDriver();
144 
145     QMutexLocker lock(&gTinyCan->mutex);
146     gTinyCan->channels.removeAll(this);
147 }
148 
149 struct BitrateItem
150 {
151     int bitrate;
152     int code;
153 };
154 
155 struct BitrateLessFunctor
156 {
operator ()BitrateLessFunctor157     bool operator()( const BitrateItem &item1, const BitrateItem &item2) const
158     {
159         return item1.bitrate < item2.bitrate;
160     }
161 };
162 
bitrateCodeFromBitrate(int bitrate)163 static int bitrateCodeFromBitrate(int bitrate)
164 {
165     static const BitrateItem bitratetable[] = {
166         { 10000, CAN_10K_BIT },
167         { 20000, CAN_20K_BIT },
168         { 50000, CAN_50K_BIT },
169         { 100000, CAN_100K_BIT },
170         { 125000, CAN_125K_BIT },
171         { 250000, CAN_250K_BIT },
172         { 500000, CAN_500K_BIT },
173         { 800000, CAN_800K_BIT },
174         { 1000000, CAN_1M_BIT }
175     };
176 
177     static const BitrateItem *endtable = bitratetable + (sizeof(bitratetable) / sizeof(*bitratetable));
178 
179     const BitrateItem item = { bitrate , 0 };
180     const BitrateItem *where = std::lower_bound(bitratetable, endtable, item, BitrateLessFunctor());
181     return where != endtable ? where->code : -1;
182 }
183 
open()184 bool 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(systemErrorString(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(systemErrorString(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(systemErrorString(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 
close()221 void 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(systemErrorString(ret), QCanBusDevice::CanBusError::ConnectionError);
231 
232     isOpen = false;
233 }
234 
setConfigurationParameter(int key,const QVariant & value)235 bool 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(TinyCanBackend::tr("Unsupported configuration key: %1").arg(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.
systemErrorString(int errorCode)251 QString TinyCanBackendPrivate::systemErrorString(int errorCode)
252 {
253     switch (errorCode) {
254     case 0:
255         return TinyCanBackend::tr("No error");
256     case -1:
257         return TinyCanBackend::tr("Driver not initialized");
258     case -2:
259         return TinyCanBackend::tr("Invalid parameters values were passed");
260     case -3:
261         return TinyCanBackend::tr("Invalid index value");
262     case -4:
263         return TinyCanBackend::tr("More invalid CAN-channel");
264     case -5:
265         return TinyCanBackend::tr("General error");
266     case -6:
267         return TinyCanBackend::tr("The FIFO cannot be written");
268     case -7:
269         return TinyCanBackend::tr("The buffer cannot be written");
270     case -8:
271         return TinyCanBackend::tr("The FIFO cannot be read");
272     case -9:
273         return TinyCanBackend::tr("The buffer cannot be read");
274     case -10:
275         return TinyCanBackend::tr("Variable not found");
276     case -11:
277         return TinyCanBackend::tr("Reading of the variable does not permit");
278     case -12:
279         return TinyCanBackend::tr("Reading buffer for variable too small");
280     case -13:
281         return TinyCanBackend::tr("Writing of the variable does not permit");
282     case -14:
283         return TinyCanBackend::tr("The string/stream to be written is to majority");
284     case -15:
285         return TinyCanBackend::tr("Fell short min of value");
286     case -16:
287         return TinyCanBackend::tr("Max value crossed");
288     case -17:
289         return TinyCanBackend::tr("Access refuses");
290     case -18:
291         return TinyCanBackend::tr("Invalid value of CAN speed");
292     case -19:
293         return TinyCanBackend::tr("Invalid value of baud rate");
294     case -20:
295         return TinyCanBackend::tr("Value not put");
296     case -21:
297         return TinyCanBackend::tr("No connection to the hardware");
298     case -22:
299         return TinyCanBackend::tr("Communication error to the hardware");
300     case -23:
301         return TinyCanBackend::tr("Hardware sends wrong number of parameters");
302     case -24:
303         return TinyCanBackend::tr("Not enough main memory");
304     case -25:
305         return TinyCanBackend::tr("The system cannot provide the enough resources");
306     case -26:
307         return TinyCanBackend::tr("A system call returns with an error");
308     case -27:
309         return TinyCanBackend::tr("The main thread is occupied");
310     case -28:
311         return TinyCanBackend::tr("User allocated memory not found");
312     case -29:
313         return TinyCanBackend::tr("The main thread cannot be launched");
314         // the codes -30...-33 are skipped, as they belongs to sockets
315     default:
316         return TinyCanBackend::tr("Unknown error");
317     }
318 }
319 
channelIndexFromName(const QString & interfaceName)320 static 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 
setupChannel(const QString & interfaceName)330 void TinyCanBackendPrivate::setupChannel(const QString &interfaceName)
331 {
332     channelIndex = channelIndexFromName(interfaceName);
333 }
334 
335 // Calls only in constructor
setupDefaultConfigurations()336 void TinyCanBackendPrivate::setupDefaultConfigurations()
337 {
338     Q_Q(TinyCanBackend);
339 
340     q->setConfigurationParameter(QCanBusDevice::BitRateKey, 500000);
341 }
342 
startWrite()343 void 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(message.Data.Bytes, payload.constData(), sizeof(message.Data.Bytes));
369         const int ret = ::CanTransmit(channelIndex, &message, messagesToWrite);
370         if (Q_UNLIKELY(ret < 0))
371             q->setError(systemErrorString(ret), QCanBusDevice::CanBusError::WriteError);
372         else
373             emit q->framesWritten(messagesToWrite);
374     }
375 
376     if (q->hasOutgoingFrames() && !writeNotifier->isActive())
377         writeNotifier->start();
378 }
379 
380 // this method is called from the different thread!
startRead()381 void 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(systemErrorString(ret), QCanBusDevice::CanBusError::ReadError);
397 
398             TDeviceStatus status = {};
399 
400             if (::CanGetDeviceStatus(channelIndex, &status) < 0) {
401                 q->setError(systemErrorString(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(std::move(frame));
425     }
426 
427     q->enqueueReceivedFrames(newFrames);
428 }
429 
startupDriver()430 void 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(systemErrorString(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 
cleanupDriver()453 void 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 
resetController()467 void 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(ret);
473         qCWarning(QT_CANBUS_PLUGINS_TINYCAN, "Cannot perform hardware reset: %ls",
474                   qUtf16Printable(errorString));
475         q->setError(errorString, QCanBusDevice::CanBusError::ConfigurationError);
476     }
477 }
478 
setBitRate(int bitrate)479 bool 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(TinyCanBackend::tr("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(systemErrorString(ret), QCanBusDevice::CanBusError::ConfigurationError);
494             return false;
495         }
496     }
497 
498     return true;
499 }
500 
TinyCanBackend(const QString & name,QObject * parent)501 TinyCanBackend::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(&TinyCanBackend::resetController, this);
511     setResetControllerFunction(f);
512 }
513 
~TinyCanBackend()514 TinyCanBackend::~TinyCanBackend()
515 {
516     close();
517     delete d_ptr;
518 }
519 
open()520 bool 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, 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 
close()546 void TinyCanBackend::close()
547 {
548     Q_D(TinyCanBackend);
549 
550     d->close();
551 
552     setState(QCanBusDevice::UnconnectedState);
553 }
554 
setConfigurationParameter(int key,const QVariant & value)555 void 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 
writeFrame(const QCanBusFrame & newData)563 bool 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(tr("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(tr("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(tr("CAN FD frame format not supported."), QCanBusDevice::WriteError);
586         return false;
587     }
588 
589     enqueueOutgoingFrame(newData);
590 
591     if (!d->writeNotifier->isActive())
592         d->writeNotifier->start();
593 
594     return true;
595 }
596 
597 // TODO: Implement me
interpretErrorFrame(const QCanBusFrame & errorFrame)598 QString TinyCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame)
599 {
600     Q_UNUSED(errorFrame);
601 
602     return QString();
603 }
604 
resetController()605 void TinyCanBackend::resetController()
606 {
607     Q_D(TinyCanBackend);
608     d->resetController();
609 }
610 
611 QT_END_NAMESPACE
612