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 | |
43 | namespace { |
44 | |
45 | enum 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. |
52 | template <typename T> |
53 | struct 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. |
69 | const int StringBufferSize = 256; |
70 | |
71 | } // anonymous namespace |
72 | |
73 | Q_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_PASSTHRU, "qt.canbus.plugins.passthru" , QtWarningMsg) |
74 | |
75 | namespace J2534 { |
76 | |
77 | Message::Message() |
78 | { |
79 | std::memset(s: m_data, c: 0, n: sizeof(m_data)); |
80 | } |
81 | |
82 | Message::Message(Protocol proto) |
83 | : m_protocolId (ulong(proto)) |
84 | { |
85 | std::memset(s: m_data, c: 0, n: sizeof(m_data)); |
86 | } |
87 | |
88 | PassThru::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 | |
110 | PassThru::~PassThru() |
111 | { |
112 | m_libJ2534.unload(); |
113 | } |
114 | |
115 | PassThru::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 | |
124 | PassThru::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 | |
132 | PassThru::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 | |
142 | PassThru::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 | |
150 | PassThru::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 | |
159 | PassThru::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 | |
168 | PassThru::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 | |
181 | PassThru::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 | |
190 | PassThru::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 | |
198 | QString PassThru::lastErrorString() const |
199 | { |
200 | return m_lastErrorString; |
201 | } |
202 | |
203 | PassThru::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 | |