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