diff options
Diffstat (limited to 'src/plugins/canbus/passthrucan/j2534passthru.cpp')
-rw-r--r-- | src/plugins/canbus/passthrucan/j2534passthru.cpp | 222 |
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 |