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

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