// 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 #include "j2534passthru.h" #include #include namespace { enum Ioctl { GetConfig = 1, SetConfig = 2 }; // Template to model the structs SCONFIG_LIST, SBYTE_ARRAY etc as defined // in the J2534 spec. template 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 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