Revision control

++ // Copyright (C) 2017 Ford Motor Company.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #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
++
++ QT_BEGIN_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
++
++ QT_END_NAMESPACE