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 "j2534passthru.h"
38
39#include <QtEndian>
40
41#include <cstring>
42
43namespace {
44
45enum Ioctl {
46 GetConfig = 1,
47 SetConfig = 2
48};
49
50// Template to model the structs SCONFIG_LIST, SBYTE_ARRAY etc as defined
51// in the J2534 spec.
52template <typename T>
53struct SArray
54{
55 SArray(ulong n, T *p) : num(n), ptr(p) {}
56 // On Windows x64, ulong is 32 bit wide and thus the value would normally
57 // be padded so that the pointer begins on a 64-bit boundary. It is not
58 // clear from the J2534 spec whether structs should be packed or not on
59 // x64. Most vendors still only provide a 32-bit DLL, but there is at
60 // least one x64 implementation (from Hatteland Display) out there which
61 // does not pack this struct.
62 ulong num;
63 T *ptr;
64};
65
66// Fixed-length string buffers must be at least 80 bytes according to the spec.
67// The example code in the spec document uses 256 bytes though -- let's play it
68// safe and do so, too.
69const int StringBufferSize = 256;
70
71} // anonymous namespace
72
73Q_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_PASSTHRU, "qt.canbus.plugins.passthru", QtWarningMsg)
74
75namespace J2534 {
76
77Message::Message()
78{
79 std::memset(s: m_data, c: 0, n: sizeof(m_data));
80}
81
82Message::Message(Protocol proto)
83 : m_protocolId (ulong(proto))
84{
85 std::memset(s: m_data, c: 0, n: sizeof(m_data));
86}
87
88PassThru::PassThru(const QString &libraryPath, QObject *parent)
89 : QObject(parent)
90 , m_libJ2534 (libraryPath, this)
91{
92 if (!m_libJ2534.load()
93 || !resolveApiFunction(funcPtr: &m_ptOpen, name: "PassThruOpen")
94 || !resolveApiFunction(funcPtr: &m_ptClose, name: "PassThruClose")
95 || !resolveApiFunction(funcPtr: &m_ptConnect, name: "PassThruConnect")
96 || !resolveApiFunction(funcPtr: &m_ptDisconnect, name: "PassThruDisconnect")
97 || !resolveApiFunction(funcPtr: &m_ptReadMsgs, name: "PassThruReadMsgs")
98 || !resolveApiFunction(funcPtr: &m_ptWriteMsgs, name: "PassThruWriteMsgs")
99 || !resolveApiFunction(funcPtr: &m_ptStartMsgFilter, name: "PassThruStartMsgFilter")
100 || !resolveApiFunction(funcPtr: &m_ptGetLastError, name: "PassThruGetLastError")
101 || !resolveApiFunction(funcPtr: &m_ptIoctl, name: "PassThruIoctl")) {
102
103 m_lastError = LoadFailed;
104 m_lastErrorString = m_libJ2534.errorString();
105
106 qCWarning(QT_CANBUS_PLUGINS_PASSTHRU, "%ls", qUtf16Printable(m_lastErrorString));
107 }
108}
109
110PassThru::~PassThru()
111{
112 m_libJ2534.unload();
113}
114
115PassThru::Status PassThru::open(const QByteArray &name, Handle *deviceId)
116{
117 Q_ASSERT(m_ptOpen);
118
119 const char *const devName = (name.isEmpty()) ? nullptr : name.data();
120 const long status = (*m_ptOpen)(devName, deviceId);
121 return handleResult(statusCode: status);
122}
123
124PassThru::Status PassThru::close(Handle deviceId)
125{
126 Q_ASSERT(m_ptClose);
127
128 const long status = (*m_ptClose)(deviceId);
129 return handleResult(statusCode: status);
130}
131
132PassThru::Status PassThru::connect(Handle deviceId, Protocol protocolId,
133 ConnectFlags flags, uint baudRate, Handle *channelId)
134{
135 Q_ASSERT(m_ptConnect);
136
137 const long status = (*m_ptConnect)(deviceId, ulong(protocolId),
138 flags, baudRate, channelId);
139 return handleResult(statusCode: status);
140}
141
142PassThru::Status PassThru::disconnect(Handle channelId)
143{
144 Q_ASSERT(m_ptDisconnect);
145
146 const long status = (*m_ptDisconnect)(channelId);
147 return handleResult(statusCode: status);
148}
149
150PassThru::Status PassThru::readMsgs(Handle channelId, Message *msgs,
151 ulong *numMsgs, uint timeout)
152{
153 Q_ASSERT(m_ptReadMsgs);
154
155 const long status = (*m_ptReadMsgs)(channelId, msgs, numMsgs, timeout);
156 return handleResult(statusCode: status);
157}
158
159PassThru::Status PassThru::writeMsgs(Handle channelId, const Message *msgs,
160 ulong *numMsgs, uint timeout)
161{
162 Q_ASSERT(m_ptWriteMsgs);
163
164 const long status = (*m_ptWriteMsgs)(channelId, msgs, numMsgs, timeout);
165 return handleResult(statusCode: status);
166}
167
168PassThru::Status PassThru::startMsgFilter(Handle channelId, FilterType filterType,
169 const Message &maskMsg, const Message &patternMsg)
170{
171 Q_ASSERT(m_ptStartMsgFilter);
172
173 // The CAN pass-thru plugin implementation does not need the filter ID.
174 Handle filterId = 0;
175
176 const long status = (*m_ptStartMsgFilter)(channelId, filterType, &maskMsg,
177 &patternMsg, nullptr, &filterId);
178 return handleResult(statusCode: status);
179}
180
181PassThru::Status PassThru::setConfig(Handle channelId, const Config *params, ulong numParams)
182{
183 Q_ASSERT(m_ptIoctl);
184
185 const SArray<const Config> configList {numParams, params};
186 const long status = (*m_ptIoctl)(channelId, SetConfig, &configList, nullptr);
187 return handleResult(statusCode: status);
188}
189
190PassThru::Status PassThru::clear(Handle channelId, ClearTarget target)
191{
192 Q_ASSERT(m_ptIoctl);
193
194 const long status = (*m_ptIoctl)(channelId, target, nullptr, nullptr);
195 return handleResult(statusCode: status);
196}
197
198QString PassThru::lastErrorString() const
199{
200 return m_lastErrorString;
201}
202
203PassThru::Status PassThru::handleResult(long statusCode)
204{
205 if (Q_UNLIKELY(statusCode != NoError)) {
206 m_lastError = Status(statusCode);
207
208 QByteArray description (StringBufferSize, 0);
209 Q_ASSERT(m_ptGetLastError);
210 const long descStatus = (*m_ptGetLastError)(description.data());
211
212 if (Q_LIKELY(descStatus == NoError)) {
213 m_lastErrorString = QString::fromLatin1(str: description);
214 } else {
215 m_lastErrorString = tr(s: "Command failed with status code %1").arg(a: statusCode);
216 qCWarning(QT_CANBUS_PLUGINS_PASSTHRU, "GetLastError failed with code %ld", descStatus);
217 }
218 }
219 return Status(statusCode);
220}
221
222} // namespace J2534
223

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