1/****************************************************************************
2**
3** Copyright (C) 2017 Ford Motor Company.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the QtSerialBus module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "passthrucanio.h"
38
39#include <QLoggingCategory>
40#include <QtEndian>
41
42#include <cstring>
43
44PassThruCanIO::PassThruCanIO(QObject *parent)
45 : QObject(parent)
46 , m_ioBuffer (8, J2534::Message(J2534::Protocol::CAN))
47{
48}
49
50PassThruCanIO::~PassThruCanIO()
51{
52}
53
54void PassThruCanIO::open(const QString &library, const QByteArray &subDev, uint bitRate)
55{
56 if (Q_UNLIKELY(m_passThru)) {
57 qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Pass-thru interface already open");
58 emit openFinished(success: false);
59 return;
60 }
61 qCDebug(QT_CANBUS_PLUGINS_PASSTHRU, "Loading interface library: %ls",
62 qUtf16Printable(library));
63
64 m_passThru = new J2534::PassThru(library, this);
65 J2534::PassThru::Status openStatus = m_passThru->lastError();
66
67 if (openStatus == J2534::PassThru::NoError)
68 openStatus = m_passThru->open(name: subDev, deviceId: &m_deviceId);
69
70 if (openStatus == J2534::PassThru::NoError
71 && m_passThru->connect(deviceId: m_deviceId, protocolId: J2534::Protocol::CAN,
72 flags: J2534::PassThru::CAN29BitID | J2534::PassThru::CANIDBoth,
73 baudRate: bitRate, channelId: &m_channelId) == J2534::PassThru::NoError) {
74 emit openFinished(success: true);
75 return;
76 }
77 emit errorOccurred(description: m_passThru->lastErrorString(),
78 error: QCanBusDevice::ConnectionError);
79
80 if (openStatus == J2534::PassThru::NoError
81 && m_passThru->close(deviceId: m_deviceId) != J2534::PassThru::NoError)
82 qCWarning(QT_CANBUS_PLUGINS_PASSTHRU, "Failed to close pass-thru device");
83
84 delete m_passThru;
85 m_passThru = nullptr;
86
87 emit openFinished(success: false);
88}
89
90void PassThruCanIO::close()
91{
92 if (Q_LIKELY(m_passThru)) {
93 delete m_idleNotifier;
94 m_idleNotifier = nullptr;
95
96 if (m_passThru->disconnect(channelId: m_channelId) != J2534::PassThru::NoError
97 || m_passThru->close(deviceId: m_deviceId) != J2534::PassThru::NoError) {
98
99 qCWarning(QT_CANBUS_PLUGINS_PASSTHRU, "Failed to close pass-thru device");
100 emit errorOccurred(description: m_passThru->lastErrorString(),
101 error: QCanBusDevice::ConnectionError);
102 }
103 delete m_passThru;
104 m_passThru = nullptr;
105 }
106 emit closeFinished();
107}
108
109void PassThruCanIO::applyConfig(int key, const QVariant &value)
110{
111 if (Q_UNLIKELY(!m_passThru)) {
112 qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Pass-thru interface not open");
113 return;
114 }
115 bool success = true;
116
117 switch (key) {
118 case QCanBusDevice::RawFilterKey:
119 success = setMessageFilters(qvariant_cast<QList<QCanBusDevice::Filter>>(v: value));
120 break;
121 case QCanBusDevice::LoopbackKey:
122 success = setConfigValue(param: J2534::Config::Loopback, value: value.toBool());
123 break;
124 case QCanBusDevice::BitRateKey:
125 success = setConfigValue(param: J2534::Config::DataRate, value: value.toUInt());
126 break;
127 default:
128 emit errorOccurred(description: tr(s: "Unsupported configuration key: %1").arg(a: key),
129 error: QCanBusDevice::ConfigurationError);
130 break;
131 }
132 if (!success) {
133 emit errorOccurred(description: tr(s: "Configuration failed: %1").arg(a: m_passThru->lastErrorString()),
134 error: QCanBusDevice::ConfigurationError);
135 }
136}
137
138void PassThruCanIO::listen()
139{
140 if (Q_UNLIKELY(!m_passThru)) {
141 qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Pass-thru interface not open");
142 return;
143 }
144 if (Q_UNLIKELY(m_idleNotifier)) {
145 qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Idle notifier already created");
146 return;
147 }
148 m_idleNotifier = new QTimer(this);
149 connect(sender: m_idleNotifier, signal: &QTimer::timeout, receiver: this, slot: &PassThruCanIO::pollForMessages);
150
151 m_idleNotifier->start(msec: 0);
152}
153
154bool PassThruCanIO::enqueueMessage(const QCanBusFrame &frame)
155{
156 const QMutexLocker lock (&m_writeGuard);
157 m_writeQueue.append(t: frame);
158 return true;
159}
160
161bool PassThruCanIO::setMessageFilters(const QList<QCanBusDevice::Filter> &filters)
162{
163 if (m_passThru->clear(channelId: m_channelId, target: J2534::PassThru::MsgFilters) != J2534::PassThru::NoError)
164 return false;
165
166 J2534::Message pattern {J2534::Protocol::CAN};
167 pattern.setSize(4);
168 J2534::Message mask {J2534::Protocol::CAN};
169 mask.setSize(4);
170
171 for (const auto &filter : filters) {
172 if (filter.type != QCanBusFrame::DataFrame
173 && filter.type != QCanBusFrame::InvalidFrame) {
174 emit errorOccurred(description: tr(s: "Configuration failed: unsupported filter type"),
175 error: QCanBusDevice::ConfigurationError);
176 break;
177 }
178 if (filter.format & QCanBusDevice::Filter::MatchExtendedFormat)
179 pattern.setRxStatus(J2534::Message::InCAN29BitID);
180 else
181 pattern.setRxStatus({});
182
183 if (filter.format != QCanBusDevice::Filter::MatchBaseAndExtendedFormat)
184 mask.setRxStatus(J2534::Message::InCAN29BitID);
185 else
186 mask.setRxStatus({});
187
188 qToBigEndian<quint32>(src: filter.frameId & filter.frameIdMask, dest: pattern.data());
189 qToBigEndian<quint32>(src: filter.frameIdMask, dest: mask.data());
190
191 if (m_passThru->startMsgFilter(channelId: m_channelId, filterType: J2534::PassThru::PassFilter,
192 maskMsg: mask, patternMsg: pattern) != J2534::PassThru::NoError)
193 return false;
194 }
195 return true;
196}
197
198bool PassThruCanIO::setConfigValue(J2534::Config::Parameter param, ulong value)
199{
200 const J2534::Config config {param, value};
201
202 return (m_passThru->setConfig(channelId: m_channelId, params: &config) == J2534::PassThru::NoError);
203}
204
205void PassThruCanIO::pollForMessages()
206{
207 if (Q_UNLIKELY(!m_passThru)) {
208 qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Pass-thru interface not open");
209 return;
210 }
211 const bool writePending = writeMessages();
212 readMessages(writePending);
213}
214
215bool PassThruCanIO::writeMessages()
216{
217 ulong numMsgs = m_ioBuffer.size();
218 {
219 const QMutexLocker lock (&m_writeGuard);
220 numMsgs = qMin<ulong>(a: m_writeQueue.size(), b: numMsgs);
221
222 for (ulong i = 0; i < numMsgs; ++i) {
223 const QCanBusFrame &frame = m_writeQueue.at(i);
224 J2534::Message &msg = m_ioBuffer[i];
225
226 const QByteArray payload = frame.payload();
227 const ulong payloadSize = qMin<ulong>(a: payload.size(),
228 b: J2534::Message::maxSize - 4);
229 msg.setRxStatus({});
230 msg.setTimestamp(0);
231 msg.setSize(4 + payloadSize);
232 msg.setExtraDataIndex(0);
233
234 if (frame.hasExtendedFrameFormat())
235 msg.setTxFlags(J2534::Message::OutCAN29BitID);
236 else
237 msg.setTxFlags({});
238
239 qToBigEndian<quint32>(src: frame.frameId(), dest: msg.data());
240 std::memcpy(dest: msg.data() + 4, src: payload.data(), n: payloadSize);
241 }
242 }
243 if (numMsgs == 0)
244 return false;
245
246 const auto status = m_passThru->writeMsgs(channelId: m_channelId, msgs: m_ioBuffer.constData(),
247 numMsgs: &numMsgs, timeout: pollTimeout);
248 if (status == J2534::PassThru::BufferFull)
249 return false;
250
251 if (status != J2534::PassThru::NoError && status != J2534::PassThru::Timeout) {
252 emit errorOccurred(description: tr(s: "Message write failed: %1").arg(a: m_passThru->lastErrorString()),
253 error: QCanBusDevice::WriteError);
254 return false;
255 }
256 if (numMsgs == 0)
257 return false;
258
259 bool morePending;
260 {
261 const QMutexLocker lock (&m_writeGuard);
262 // De-queue successfully written frames.
263 m_writeQueue.erase(afirst: m_writeQueue.begin(), alast: m_writeQueue.begin() + numMsgs);
264 morePending = !m_writeQueue.isEmpty();
265 }
266 emit messagesSent(count: numMsgs);
267
268 return morePending;
269}
270
271void PassThruCanIO::readMessages(bool writePending)
272{
273 // If there are outgoing messages waiting to be written, just check
274 // for already received messages but do not block waiting for more.
275 const uint timeout = (writePending) ? 0 : pollTimeout;
276
277 ulong numMsgs = m_ioBuffer.size();
278 const auto status = m_passThru->readMsgs(channelId: m_channelId, msgs: m_ioBuffer.data(),
279 numMsgs: &numMsgs, timeout);
280 if (status == J2534::PassThru::BufferEmpty)
281 return;
282
283 if (status != J2534::PassThru::NoError && status != J2534::PassThru::Timeout) {
284 emit errorOccurred(description: tr(s: "Message read failed: %1").arg(a: m_passThru->lastErrorString()),
285 error: QCanBusDevice::ReadError);
286 if (status != J2534::PassThru::BufferOverflow)
287 return;
288 }
289 const int numFrames = qMin<ulong>(a: m_ioBuffer.size(), b: numMsgs);
290 QVector<QCanBusFrame> frames;
291 frames.reserve(asize: numFrames);
292
293 for (int i = 0; i < numFrames; ++i) {
294 const J2534::Message &msg = m_ioBuffer.at(i);
295 if (Q_UNLIKELY(msg.size() < 4)
296 || Q_UNLIKELY(msg.size() > J2534::Message::maxSize)) {
297 // This normally shouldn't happen, so a log message is appropriate.
298 qCWarning(QT_CANBUS_PLUGINS_PASSTHRU,
299 "Message with invalid size %lu received", msg.size());
300 continue;
301 }
302 const quint32 msgId = qFromBigEndian<quint32>(src: msg.data());
303 const QByteArray payload (msg.data() + 4, msg.size() - 4);
304
305 QCanBusFrame frame (msgId, payload);
306 frame.setExtendedFrameFormat((msg.rxStatus() & J2534::Message::InCAN29BitID) != 0);
307 frame.setLocalEcho((msg.rxStatus() & J2534::Message::InTxMsgType) != 0);
308 frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(usec: msg.timestamp()));
309
310 frames.append(t: std::move(frame));
311 }
312 if (!frames.isEmpty())
313 emit messagesReceived(frames: std::move(frames));
314}
315

source code of qtserialbus/src/plugins/canbus/passthrucan/passthrucanio.cpp