summaryrefslogtreecommitdiffstats
path: root/src/plugins/canbus/passthrucan/j2534passthru.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/canbus/passthrucan/j2534passthru.cpp')
-rw-r--r--src/plugins/canbus/passthrucan/j2534passthru.cpp222
1 files changed, 222 insertions, 0 deletions
diff --git a/src/plugins/canbus/passthrucan/j2534passthru.cpp b/src/plugins/canbus/passthrucan/j2534passthru.cpp
new file mode 100644
index 0000000..22bf996
--- /dev/null
+++ b/src/plugins/canbus/passthrucan/j2534passthru.cpp
@@ -0,0 +1,222 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Ford Motor Company.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtSerialBus module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "j2534passthru.h"
+
+#include <QtEndian>
+
+#include <cstring>
+
+namespace {
+
+enum Ioctl {
+ GetConfig = 1,
+ SetConfig = 2
+};
+
+// Template to model the structs SCONFIG_LIST, SBYTE_ARRAY etc as defined
+// in the J2534 spec.
+template <typename T>
+struct SArray
+{
+ SArray(ulong n, T *p) : num(n), ptr(p) {}
+ // On Windows x64, ulong is 32 bit wide and thus the value would normally
+ // be padded so that the pointer begins on a 64-bit boundary. It is not
+ // clear from the J2534 spec whether structs should be packed or not on
+ // x64. Most vendors still only provide a 32-bit DLL, but there is at
+ // least one x64 implementation (from Hatteland Display) out there which
+ // does not pack this struct.
+ ulong num;
+ T *ptr;
+};
+
+// Fixed-length string buffers must be at least 80 bytes according to the spec.
+// The example code in the spec document uses 256 bytes though -- let's play it
+// safe and do so, too.
+const int StringBufferSize = 256;
+
+} // anonymous namespace
+
+Q_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_PASSTHRU, "qt.canbus.plugins.passthru", QtWarningMsg)
+
+namespace J2534 {
+
+Message::Message()
+{
+ std::memset(m_data, 0, sizeof(m_data));
+}
+
+Message::Message(Protocol proto)
+ : m_protocolId (ulong(proto))
+{
+ std::memset(m_data, 0, sizeof(m_data));
+}
+
+PassThru::PassThru(const QString &libraryPath, QObject *parent)
+ : QObject(parent)
+ , m_libJ2534 (libraryPath, this)
+{
+ if (!m_libJ2534.load()
+ || !resolveApiFunction(&m_ptOpen, "PassThruOpen")
+ || !resolveApiFunction(&m_ptClose, "PassThruClose")
+ || !resolveApiFunction(&m_ptConnect, "PassThruConnect")
+ || !resolveApiFunction(&m_ptDisconnect, "PassThruDisconnect")
+ || !resolveApiFunction(&m_ptReadMsgs, "PassThruReadMsgs")
+ || !resolveApiFunction(&m_ptWriteMsgs, "PassThruWriteMsgs")
+ || !resolveApiFunction(&m_ptStartMsgFilter, "PassThruStartMsgFilter")
+ || !resolveApiFunction(&m_ptGetLastError, "PassThruGetLastError")
+ || !resolveApiFunction(&m_ptIoctl, "PassThruIoctl")) {
+
+ m_lastError = LoadFailed;
+ m_lastErrorString = m_libJ2534.errorString();
+
+ qCWarning(QT_CANBUS_PLUGINS_PASSTHRU, "%ls", qUtf16Printable(m_lastErrorString));
+ }
+}
+
+PassThru::~PassThru()
+{
+ m_libJ2534.unload();
+}
+
+PassThru::Status PassThru::open(const QByteArray &name, Handle *deviceId)
+{
+ Q_ASSERT(m_ptOpen);
+
+ const char *const devName = (name.isEmpty()) ? nullptr : name.data();
+ const long status = (*m_ptOpen)(devName, deviceId);
+ return handleResult(status);
+}
+
+PassThru::Status PassThru::close(Handle deviceId)
+{
+ Q_ASSERT(m_ptClose);
+
+ const long status = (*m_ptClose)(deviceId);
+ return handleResult(status);
+}
+
+PassThru::Status PassThru::connect(Handle deviceId, Protocol protocolId,
+ ConnectFlags flags, uint baudRate, Handle *channelId)
+{
+ Q_ASSERT(m_ptConnect);
+
+ const long status = (*m_ptConnect)(deviceId, ulong(protocolId),
+ flags, baudRate, channelId);
+ return handleResult(status);
+}
+
+PassThru::Status PassThru::disconnect(Handle channelId)
+{
+ Q_ASSERT(m_ptDisconnect);
+
+ const long status = (*m_ptDisconnect)(channelId);
+ return handleResult(status);
+}
+
+PassThru::Status PassThru::readMsgs(Handle channelId, Message *msgs,
+ ulong *numMsgs, uint timeout)
+{
+ Q_ASSERT(m_ptReadMsgs);
+
+ const long status = (*m_ptReadMsgs)(channelId, msgs, numMsgs, timeout);
+ return handleResult(status);
+}
+
+PassThru::Status PassThru::writeMsgs(Handle channelId, const Message *msgs,
+ ulong *numMsgs, uint timeout)
+{
+ Q_ASSERT(m_ptWriteMsgs);
+
+ const long status = (*m_ptWriteMsgs)(channelId, msgs, numMsgs, timeout);
+ return handleResult(status);
+}
+
+PassThru::Status PassThru::startMsgFilter(Handle channelId, FilterType filterType,
+ const Message &maskMsg, const Message &patternMsg)
+{
+ Q_ASSERT(m_ptStartMsgFilter);
+
+ // The CAN pass-thru plugin implementation does not need the filter ID.
+ Handle filterId = 0;
+
+ const long status = (*m_ptStartMsgFilter)(channelId, filterType, &maskMsg,
+ &patternMsg, nullptr, &filterId);
+ return handleResult(status);
+}
+
+PassThru::Status PassThru::setConfig(Handle channelId, const Config *params, ulong numParams)
+{
+ Q_ASSERT(m_ptIoctl);
+
+ const SArray<const Config> configList {numParams, params};
+ const long status = (*m_ptIoctl)(channelId, SetConfig, &configList, nullptr);
+ return handleResult(status);
+}
+
+PassThru::Status PassThru::clear(Handle channelId, ClearTarget target)
+{
+ Q_ASSERT(m_ptIoctl);
+
+ const long status = (*m_ptIoctl)(channelId, target, nullptr, nullptr);
+ return handleResult(status);
+}
+
+QString PassThru::lastErrorString() const
+{
+ return m_lastErrorString;
+}
+
+PassThru::Status PassThru::handleResult(long statusCode)
+{
+ if (Q_UNLIKELY(statusCode != NoError)) {
+ m_lastError = Status(statusCode);
+
+ QByteArray description (StringBufferSize, 0);
+ Q_ASSERT(m_ptGetLastError);
+ const long descStatus = (*m_ptGetLastError)(description.data());
+
+ if (Q_LIKELY(descStatus == NoError)) {
+ m_lastErrorString = QString::fromLatin1(description);
+ } else {
+ m_lastErrorString = tr("Command failed with status code %1").arg(statusCode);
+ qCWarning(QT_CANBUS_PLUGINS_PASSTHRU, "GetLastError failed with code %ld", descStatus);
+ }
+ }
+ return Status(statusCode);
+}
+
+} // namespace J2534