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

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