diff options
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/canbus/canbus.pro | 2 | ||||
-rw-r--r-- | src/plugins/canbus/passthrucan/j2534passthru.cpp | 222 | ||||
-rw-r--r-- | src/plugins/canbus/passthrucan/j2534passthru.h | 332 | ||||
-rw-r--r-- | src/plugins/canbus/passthrucan/main.cpp | 71 | ||||
-rw-r--r-- | src/plugins/canbus/passthrucan/passthrucan.pro | 21 | ||||
-rw-r--r-- | src/plugins/canbus/passthrucan/passthrucanbackend.cpp | 273 | ||||
-rw-r--r-- | src/plugins/canbus/passthrucan/passthrucanbackend.h | 84 | ||||
-rw-r--r-- | src/plugins/canbus/passthrucan/passthrucanio.cpp | 314 | ||||
-rw-r--r-- | src/plugins/canbus/passthrucan/passthrucanio.h | 98 | ||||
-rw-r--r-- | src/plugins/canbus/passthrucan/plugin.json | 3 |
10 files changed, 1419 insertions, 1 deletions
diff --git a/src/plugins/canbus/canbus.pro b/src/plugins/canbus/canbus.pro index 37db8a3..0eddffc 100644 --- a/src/plugins/canbus/canbus.pro +++ b/src/plugins/canbus/canbus.pro @@ -7,6 +7,6 @@ qtConfig(socketcan) { } qtConfig(library) { - SUBDIRS += peakcan tinycan + SUBDIRS += passthrucan peakcan tinycan win32:SUBDIRS += systeccan vectorcan } 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 diff --git a/src/plugins/canbus/passthrucan/j2534passthru.h b/src/plugins/canbus/passthrucan/j2534passthru.h new file mode 100644 index 0000000..a9b88f4 --- /dev/null +++ b/src/plugins/canbus/passthrucan/j2534passthru.h @@ -0,0 +1,332 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef PASSTHRUCAN_J2534PASSTHRU_H +#define PASSTHRUCAN_J2534PASSTHRU_H + +#include <QByteArray> +#include <QLibrary> +#include <QLoggingCategory> +#include <QObject> +#include <QString> + +#ifdef Q_OS_WIN32 +# define J2534_API __stdcall +#else +# define J2534_API +#endif + +Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_PASSTHRU) + +namespace J2534 { + +class Message; + +extern "C" { + +typedef long (J2534_API *PassThruOpenFunc)(const void *pName, ulong *pDeviceId); +typedef long (J2534_API *PassThruCloseFunc)(ulong deviceId); +typedef long (J2534_API *PassThruConnectFunc)(ulong deviceId, ulong protocolId, ulong flags, + ulong baudRate, ulong *pChannelId); +typedef long (J2534_API *PassThruDisconnectFunc)(ulong channelId); +typedef long (J2534_API *PassThruReadMsgsFunc)(ulong channelId, Message *pMsg, + ulong *pNumMsgs, ulong timeout); +typedef long (J2534_API *PassThruWriteMsgsFunc)(ulong channelId, const Message *pMsg, + ulong *pNumMsgs, ulong timeout); +typedef long (J2534_API *PassThruStartMsgFilterFunc)(ulong channelID, ulong filterType, + const Message *pMaskMsg, + const Message *pPatternMsg, + const Message *pFlowControlMsg, + ulong *pFilterId); +typedef long (J2534_API *PassThruGetLastErrorFunc)(char *pErrorDescription); +typedef long (J2534_API *PassThruIoctlFunc)(ulong channelId, ulong ioctlId, + const void *pInput, void *pOutput); +} // extern "C" + +enum class Protocol : uint { + J1850VPW = 1, + J1850PWM, + ISO9141, + ISO14230, + CAN, + ISO15765, + SCIAEngine, + SCIATrans, + SCIBEngine, + SCIBTrans +}; + +class Message +{ +public: + static const ulong maxSize = 4128; + + enum RxStatusBit { + InTxMsgType = 1 << 0, + InStartOfMessage = 1 << 1, + InRxBreak = 1 << 2, + InTxIndication = 1 << 3, + InISO15765PaddingError = 1 << 4, + InISO15765AddrType = 1 << 7, + InCAN29BitID = 1 << 8 + }; + Q_DECLARE_FLAGS(RxStatus, RxStatusBit) + + enum TxFlag { + OutISO15765FramePad = 1 << 6, + OutISO15765AddrType = 1 << 7, + OutCAN29BitID = 1 << 8, + OutWaitP3MinOnly = 1 << 9 + }; + Q_DECLARE_FLAGS(TxFlags, TxFlag) + + Message(); + explicit Message(Protocol proto); + + Protocol protocolId() const { return Protocol(m_protocolId); } + void setProtocolId(Protocol proto) { m_protocolId = uint(proto); } + + RxStatus rxStatus() const { return RxStatus(uint(m_rxStatus)); } + void setRxStatus(RxStatus status) { m_rxStatus = uint(status); } + + TxFlags txFlags() const { return TxFlags(uint(m_txFlags)); } + void setTxFlags(TxFlags flags) { m_txFlags = uint(flags); } + + ulong timestamp() const { return m_timestamp; } + void setTimestamp(ulong stamp) { m_timestamp = stamp; } + + ulong size() const { return m_dataSize; } + void setSize(ulong dataSize) { m_dataSize = dataSize; } + + ulong extraDataIndex() const { return m_extraDataIndex; } + void setExtraDataIndex(ulong index) { m_extraDataIndex = index; } + + char *data() { return m_data; } + const char *data() const { return m_data; } + +private: + ulong m_protocolId = 0; + ulong m_rxStatus = 0; + ulong m_txFlags = 0; + ulong m_timestamp = 0; + ulong m_dataSize = 0; + ulong m_extraDataIndex = 0; + char m_data[maxSize]; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(Message::RxStatus) +Q_DECLARE_OPERATORS_FOR_FLAGS(Message::TxFlags) + +class Config +{ +public: + enum Parameter { + DataRate = 1, + + Loopback = 3, + NodeAddress, + NetworkLine, + P1Min, + P1Max, + P2Min, + P2Max, + P3Min, + P3Max, + P4Min, + P4Max, + W1, + W2, + W3, + W4, + W5, + Tidle, + Tinil, + Twup, + Parity, + BitSamplePoint, + SyncJumpWidth, + W0, + T1Max, + T2Max, + T4Max, + T5Max, + ISO15765BS, + ISO15765STmin, + DataBits, + FiveBaudMod, + BSTx, + STminTx, + T3Max, + ISO15765WFTMax, + + CanMixedFormat = 0x8000, + J1962Pins, + + SWCANHSDataRate = 0x8010, + SWCANSpeedchangeEnable, + SWCANResSwitch, + + ActiveChannels = 0x8020, + SampleRate, + SamplesPerReading, + ReadingsPerMsg, + AveragingMethod, + SampleResolution, + InputRangeLow, + InputRangeHigh + }; + + Config() : m_parameter(0), m_value(0) {} + explicit Config(Parameter param, ulong val = 0) : m_parameter(param), m_value(val) {} + + Parameter parameter() const { return Parameter(m_parameter); } + ulong value() const { return m_value; } + +private: + ulong m_parameter; + ulong m_value; +}; + +/** + * @brief J2534 pass-through interface, version 04.04. + * @internal + * @see http://www.drewtech.com/support/passthru.html + */ +class PassThru : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(PassThru) +public: + typedef ulong Handle; + + enum Status { + LoadFailed = -1, + NoError = 0, + NotSupported, + InvalidChannelID, + InvalidProtocolID, + NullParameter, + InvalidIoctlValue, + InvalidFlags, + Failed, + DeviceNotConnected, + Timeout, + InvalidMsg, + InvalidTimeInterval, + ExceededLimit, + InvalidMsgID, + DeviceInUse, + InvalidIoctlID, + BufferEmpty, + BufferFull, + BufferOverflow, + PinInvalid, + ChannelInUse, + MsgProtocolID, + InvalidFilterID, + NoFlowControl, + NotUnique, + InvalidBaudrate, + InvalidDeviceID + }; + + enum ConnectFlag { + CAN29BitID = 1 << 8, + ISO9141NoChecksum = 1 << 9, + CANIDBoth = 1 << 11, + ISO9141KLineOnly = 1 << 12 + }; + Q_DECLARE_FLAGS(ConnectFlags, ConnectFlag) + + enum FilterType { + PassFilter = 1, + BlockFilter, + FlowControlFilter + }; + + enum ClearTarget { + TxBuffer = 7, + RxBuffer, + PeriodicMsgs, + MsgFilters + }; + + explicit PassThru(const QString &libraryPath, QObject *parent = nullptr); + virtual ~PassThru(); + + Status open(const QByteArray &name, Handle *deviceId); + Status close(Handle deviceId); + Status connect(Handle deviceId, Protocol protocolId, ConnectFlags flags, + uint baudRate, Handle *channelId); + Status disconnect(Handle channelId); + Status readMsgs(Handle channelId, Message *msgs, ulong *numMsgs, uint timeout = 0); + Status writeMsgs(Handle channelId, const Message *msgs, ulong *numMsgs, uint timeout = 0); + Status startMsgFilter(Handle channelId, FilterType filterType, + const Message &maskMsg, const Message &patternMsg); + Status setConfig(Handle channelId, const Config *params, ulong numParams = 1); + Status clear(Handle channelId, ClearTarget target); + + Status lastError() const { return m_lastError; } + QString lastErrorString() const; + +private: + Status handleResult(long statusCode); + + template <typename Func> + Func resolveApiFunction(Func *funcPtr, const char *name) { + *funcPtr = reinterpret_cast<Func>(m_libJ2534.resolve(name)); + return *funcPtr; + } + + QLibrary m_libJ2534; + PassThruOpenFunc m_ptOpen = nullptr; + PassThruCloseFunc m_ptClose = nullptr; + PassThruConnectFunc m_ptConnect = nullptr; + PassThruDisconnectFunc m_ptDisconnect = nullptr; + PassThruReadMsgsFunc m_ptReadMsgs = nullptr; + PassThruWriteMsgsFunc m_ptWriteMsgs = nullptr; + PassThruStartMsgFilterFunc m_ptStartMsgFilter = nullptr; + PassThruGetLastErrorFunc m_ptGetLastError = nullptr; + PassThruIoctlFunc m_ptIoctl = nullptr; + QString m_lastErrorString; + Status m_lastError = NoError; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(PassThru::ConnectFlags) + +} // namespace J2534 + +#endif // PASSTHRUCAN_J2534PASSTHRU_H diff --git a/src/plugins/canbus/passthrucan/main.cpp b/src/plugins/canbus/passthrucan/main.cpp new file mode 100644 index 0000000..5017597 --- /dev/null +++ b/src/plugins/canbus/passthrucan/main.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** 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 "passthrucanbackend.h" + +#include <QtSerialBus/qcanbus.h> +#include <QtSerialBus/qcanbusdevice.h> +#include <QtSerialBus/qcanbusfactory.h> + +QT_BEGIN_NAMESPACE + +class PassThruCanBusPlugin : public QObject, public QCanBusFactoryV2 +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QCanBusFactory" FILE "plugin.json") + Q_INTERFACES(QCanBusFactoryV2) + +public: + PassThruCanBusPlugin() + { + qRegisterMetaType<QCanBusDevice::CanBusError>(); + qRegisterMetaType<QVector<QCanBusFrame>>(); + } + + QList<QCanBusDeviceInfo> availableDevices(QString *) const override + { + return PassThruCanBackend::interfaces(); + } + + QCanBusDevice *createDevice(const QString &interfaceName, QString *) const override + { + return new PassThruCanBackend(interfaceName); + } +}; + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/plugins/canbus/passthrucan/passthrucan.pro b/src/plugins/canbus/passthrucan/passthrucan.pro new file mode 100644 index 0000000..96ed090 --- /dev/null +++ b/src/plugins/canbus/passthrucan/passthrucan.pro @@ -0,0 +1,21 @@ +QT = serialbus + +TARGET = qtpassthrucanbus + +SOURCES += \ + j2534passthru.cpp \ + main.cpp \ + passthrucanio.cpp \ + passthrucanbackend.cpp + +HEADERS += \ + j2534passthru.h \ + passthrucanio.h \ + passthrucanbackend.h + +DISTFILES = plugin.json + +PLUGIN_TYPE = canbus +PLUGIN_EXTENDS = serialbus +PLUGIN_CLASS_NAME = PassThruCanBusPlugin +load(qt_plugin) diff --git a/src/plugins/canbus/passthrucan/passthrucanbackend.cpp b/src/plugins/canbus/passthrucan/passthrucanbackend.cpp new file mode 100644 index 0000000..2c18160 --- /dev/null +++ b/src/plugins/canbus/passthrucan/passthrucanbackend.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** 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 "passthrucanbackend.h" +#include "passthrucanio.h" + +#include <QEventLoop> +#include <QSettings> + +namespace { + +#ifdef Q_OS_WIN32 + +static inline QString registryPath() +{ + return QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\PassThruSupport.04.04"); +} + +static QString canAdapterName(const QSettings &entries) +{ + const int supportsCan = entries.value(QStringLiteral("CAN")).toInt(); + if (supportsCan) + return entries.value(QStringLiteral("Name")).toString(); + return {}; +} + +static QString libraryForAdapter(const QString &adapterName) +{ + QString library; + QSettings entries (registryPath(), QSettings::NativeFormat); + const QStringList groups = entries.childGroups(); + + for (const auto &group : groups) { + entries.beginGroup(group); + + const QString name = canAdapterName(entries); + if (!name.isEmpty() && (adapterName.isEmpty() || + name.compare(adapterName, Qt::CaseInsensitive) == 0)) + library = entries.value(QStringLiteral("FunctionLibrary")).toString(); + + entries.endGroup(); + + if (!library.isEmpty()) + break; + } + return library; +} + +#else // !Q_OS_WIN32 + +static QString libraryForAdapter(const QString &adapterName) +{ + // Insert system-specific device name to J2534 library name mapping here. + // For now, allow the path to the J2534 library to be specified directly + // as the adapter name. + return adapterName; +} + +#endif // !Q_OS_WIN32 + +} // anonymous namespace + +PassThruCanBackend::PassThruCanBackend(const QString &name, QObject *parent) + : QCanBusDevice(parent) + , m_deviceName (name) + , m_canIO (new PassThruCanIO()) +{ + m_canIO->moveToThread(&m_ioThread); + + // Signals emitted by the I/O thread, to be queued. + connect(m_canIO, &PassThruCanIO::errorOccurred, + this, &PassThruCanBackend::setError); + connect(m_canIO, &PassThruCanIO::openFinished, + this, &PassThruCanBackend::ackOpenFinished); + connect(m_canIO, &PassThruCanIO::closeFinished, + this, &PassThruCanBackend::ackCloseFinished); + connect(m_canIO, &PassThruCanIO::messagesReceived, + this, &PassThruCanBackend::enqueueReceivedFrames); + connect(m_canIO, &PassThruCanIO::messagesSent, + this, &QCanBusDevice::framesWritten); +} + +PassThruCanBackend::~PassThruCanBackend() +{ + if (state() != UnconnectedState) { + // If the I/O thread is still active at this point, we will have to + // wait for it to finish. + QEventLoop loop; + connect(&m_ioThread, &QThread::finished, &loop, &QEventLoop::quit); + + if (state() != ClosingState) + disconnectDevice(); + + while (!m_ioThread.isFinished()) + loop.exec(QEventLoop::ExcludeUserInputEvents); + } + m_canIO->deleteLater(); +} + +void PassThruCanBackend::setConfigurationParameter(int key, const QVariant &value) +{ + QCanBusDevice::setConfigurationParameter(key, value); + + if (state() == ConnectedState) + applyConfig(key, value); +} + +bool PassThruCanBackend::writeFrame(const QCanBusFrame &frame) +{ + if (state() != ConnectedState) { + setError(tr("Device is not connected"), WriteError); + return false; + } + if (!frame.isValid()) { + setError(tr("Invalid CAN bus frame"), WriteError); + return false; + } + if (frame.frameType() != QCanBusFrame::DataFrame) { + setError(tr("Unsupported CAN frame type"), WriteError); + return false; + } + // Push the frame directly to the write queue of the worker thread, + // bypassing the QCanBusDevice output queue. Despite the duplicated + // queue, things are cleaner this way as it avoids a reverse dependency + // from the worker object on the QCanBusDevice object. + return m_canIO->enqueueMessage(frame); +} + +QString PassThruCanBackend::interpretErrorFrame(const QCanBusFrame &) +{ + // J2534 Pass-thru v04.04 does not seem to support error frames. + return {}; +} + +QList<QCanBusDeviceInfo> PassThruCanBackend::interfaces() +{ + QList<QCanBusDeviceInfo> list; +#ifdef Q_OS_WIN32 + QSettings entries (registryPath(), QSettings::NativeFormat); + const QStringList groups = entries.childGroups(); + + for (const auto &group : groups) { + entries.beginGroup(group); + + const QString name = canAdapterName(entries); + if (!name.isEmpty()) + list.append(createDeviceInfo(name)); + + entries.endGroup(); + } +#endif + return list; +} + +bool PassThruCanBackend::open() +{ + if (Q_UNLIKELY(state() != ConnectingState)) { + qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Unexpected state on open"); + return false; + } + // Support a special "adapter%subdevice" syntax to allow control of the + // device name passed to the J2534 library's PassThruOpen() function. + // If the "%subdevice" suffix is not used, the J2534 interface library + // will choose a default or ask the user. + const int splitPos = m_deviceName.indexOf(QChar::fromLatin1('%')); + const QString adapter = m_deviceName.left(splitPos); + QByteArray subDev; + + if (splitPos >= 0) + subDev = m_deviceName.midRef(splitPos + 1).toLatin1(); + + const QString library = libraryForAdapter(adapter); + if (library.isEmpty()) { + setError(tr("Adapter not found: %1").arg(adapter), ConnectionError); + return false; + } + bool ok = false; + uint bitRate = configurationParameter(BitRateKey).toUInt(&ok); + if (!ok) { + bitRate = 500*1000; // default initial bit rate + setConfigurationParameter(BitRateKey, bitRate); + } + m_ioThread.start(); + + return QMetaObject::invokeMethod(m_canIO, "open", Qt::QueuedConnection, + Q_ARG(QString, library), + Q_ARG(QByteArray, subDev), + Q_ARG(uint, bitRate)); +} + +void PassThruCanBackend::close() +{ + if (Q_UNLIKELY(state() != ClosingState)) { + qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Unexpected state on close"); + return; + } + QMetaObject::invokeMethod(m_canIO, "close", Qt::QueuedConnection); +} + +void PassThruCanBackend::ackOpenFinished(bool success) +{ + // Do not transition to connected state if close() has been called + // in the meantime. + if (state() != ConnectingState) + return; + + if (success) { + const QVariant loopback = configurationParameter(LoopbackKey); + if (loopback.toBool()) + applyConfig(LoopbackKey, loopback); + + QVariant filters = configurationParameter(RawFilterKey); + if (!filters.isValid()) { + // Configure default match-all filter. + filters = QVariant::fromValue(QList<Filter>{Filter{}}); + setConfigurationParameter(RawFilterKey, filters); + } + applyConfig(RawFilterKey, filters); + + QMetaObject::invokeMethod(m_canIO, "listen", Qt::QueuedConnection); + + setState(ConnectedState); + } else { + setState(UnconnectedState); + } +} + +void PassThruCanBackend::ackCloseFinished() +{ + m_ioThread.exit(0); + m_ioThread.wait(); + + setState(UnconnectedState); +} + +void PassThruCanBackend::applyConfig(int key, const QVariant &value) +{ + QMetaObject::invokeMethod(m_canIO, "applyConfig", Qt::QueuedConnection, + Q_ARG(int, key), Q_ARG(QVariant, value)); +} diff --git a/src/plugins/canbus/passthrucan/passthrucanbackend.h b/src/plugins/canbus/passthrucan/passthrucanbackend.h new file mode 100644 index 0000000..864d1b4 --- /dev/null +++ b/src/plugins/canbus/passthrucan/passthrucanbackend.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef PASSTHRUCAN_PASSTHRUCANBACKEND_H +#define PASSTHRUCAN_PASSTHRUCANBACKEND_H + +#include <QtSerialBus/qcanbusdevice.h> +#include <QtSerialBus/qcanbusframe.h> + +#include <QString> +#include <QThread> +#include <QVector> + +QT_BEGIN_NAMESPACE + +class PassThruCanIO; + +class PassThruCanBackend : public QCanBusDevice +{ + Q_OBJECT + Q_DISABLE_COPY(PassThruCanBackend) +public: + explicit PassThruCanBackend(const QString &name, QObject *parent = nullptr); + virtual ~PassThruCanBackend(); + + void setConfigurationParameter(int key, const QVariant &value) override; + bool writeFrame(const QCanBusFrame &frame) override; + QString interpretErrorFrame(const QCanBusFrame &errorFrame) override; + + static QList<QCanBusDeviceInfo> interfaces(); + +protected: + bool open() override; + void close() override; + +private: + void ackOpenFinished(bool success); + void ackCloseFinished(); + void applyConfig(int key, const QVariant &value); + + QString m_deviceName; + QThread m_ioThread; + PassThruCanIO * m_canIO; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QCanBusDevice::CanBusError) +Q_DECLARE_METATYPE(QVector<QCanBusFrame>) + +#endif // PASSTHRUCAN_PASSTHRUCANBACKEND_H diff --git a/src/plugins/canbus/passthrucan/passthrucanio.cpp b/src/plugins/canbus/passthrucan/passthrucanio.cpp new file mode 100644 index 0000000..dc3d2c7 --- /dev/null +++ b/src/plugins/canbus/passthrucan/passthrucanio.cpp @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** 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 "passthrucanio.h" + +#include <QLoggingCategory> +#include <QtEndian> + +#include <cstring> + +PassThruCanIO::PassThruCanIO(QObject *parent) + : QObject(parent) + , m_ioBuffer (8, J2534::Message(J2534::Protocol::CAN)) +{ +} + +PassThruCanIO::~PassThruCanIO() +{ +} + +void PassThruCanIO::open(const QString &library, const QByteArray &subDev, uint bitRate) +{ + if (Q_UNLIKELY(m_passThru)) { + qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Pass-thru interface already open"); + emit openFinished(false); + return; + } + qCDebug(QT_CANBUS_PLUGINS_PASSTHRU, "Loading interface library: %ls", + qUtf16Printable(library)); + + m_passThru = new J2534::PassThru(library, this); + J2534::PassThru::Status openStatus = m_passThru->lastError(); + + if (openStatus == J2534::PassThru::NoError) + openStatus = m_passThru->open(subDev, &m_deviceId); + + if (openStatus == J2534::PassThru::NoError + && m_passThru->connect(m_deviceId, J2534::Protocol::CAN, + J2534::PassThru::CAN29BitID | J2534::PassThru::CANIDBoth, + bitRate, &m_channelId) == J2534::PassThru::NoError) { + emit openFinished(true); + return; + } + emit errorOccurred(m_passThru->lastErrorString(), + QCanBusDevice::ConnectionError); + + if (openStatus == J2534::PassThru::NoError + && m_passThru->close(m_deviceId) != J2534::PassThru::NoError) + qCWarning(QT_CANBUS_PLUGINS_PASSTHRU, "Failed to close pass-thru device"); + + delete m_passThru; + m_passThru = nullptr; + + emit openFinished(false); +} + +void PassThruCanIO::close() +{ + if (Q_LIKELY(m_passThru)) { + delete m_idleNotifier; + m_idleNotifier = nullptr; + + if (m_passThru->disconnect(m_channelId) != J2534::PassThru::NoError + || m_passThru->close(m_deviceId) != J2534::PassThru::NoError) { + + qCWarning(QT_CANBUS_PLUGINS_PASSTHRU, "Failed to close pass-thru device"); + emit errorOccurred(m_passThru->lastErrorString(), + QCanBusDevice::ConnectionError); + } + delete m_passThru; + m_passThru = nullptr; + } + emit closeFinished(); +} + +void PassThruCanIO::applyConfig(int key, const QVariant &value) +{ + if (Q_UNLIKELY(!m_passThru)) { + qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Pass-thru interface not open"); + return; + } + bool success = true; + + switch (key) { + case QCanBusDevice::RawFilterKey: + success = setMessageFilters(qvariant_cast<QList<QCanBusDevice::Filter>>(value)); + break; + case QCanBusDevice::LoopbackKey: + success = setConfigValue(J2534::Config::Loopback, value.toBool()); + break; + case QCanBusDevice::BitRateKey: + success = setConfigValue(J2534::Config::DataRate, value.toUInt()); + break; + default: + emit errorOccurred(tr("Unsupported configuration key: %1").arg(key), + QCanBusDevice::ConfigurationError); + break; + } + if (!success) { + emit errorOccurred(tr("Configuration failed: %1").arg(m_passThru->lastErrorString()), + QCanBusDevice::ConfigurationError); + } +} + +void PassThruCanIO::listen() +{ + if (Q_UNLIKELY(!m_passThru)) { + qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Pass-thru interface not open"); + return; + } + if (Q_UNLIKELY(m_idleNotifier)) { + qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Idle notifier already created"); + return; + } + m_idleNotifier = new QTimer(this); + connect(m_idleNotifier, &QTimer::timeout, this, &PassThruCanIO::pollForMessages); + + m_idleNotifier->start(0); +} + +bool PassThruCanIO::enqueueMessage(const QCanBusFrame &frame) +{ + const QMutexLocker lock (&m_writeGuard); + m_writeQueue.append(frame); + return true; +} + +bool PassThruCanIO::setMessageFilters(const QList<QCanBusDevice::Filter> &filters) +{ + if (m_passThru->clear(m_channelId, J2534::PassThru::MsgFilters) != J2534::PassThru::NoError) + return false; + + J2534::Message pattern {J2534::Protocol::CAN}; + pattern.setSize(4); + J2534::Message mask {J2534::Protocol::CAN}; + mask.setSize(4); + + for (const auto &filter : filters) { + if (filter.type != QCanBusFrame::DataFrame + && filter.type != QCanBusFrame::InvalidFrame) { + emit errorOccurred(tr("Configuration failed: unsupported filter type"), + QCanBusDevice::ConfigurationError); + break; + } + if (filter.format & QCanBusDevice::Filter::MatchExtendedFormat) + pattern.setRxStatus(J2534::Message::InCAN29BitID); + else + pattern.setRxStatus({}); + + if (filter.format != QCanBusDevice::Filter::MatchBaseAndExtendedFormat) + mask.setRxStatus(J2534::Message::InCAN29BitID); + else + mask.setRxStatus({}); + + qToBigEndian<quint32>(filter.frameId & filter.frameIdMask, pattern.data()); + qToBigEndian<quint32>(filter.frameIdMask, mask.data()); + + if (m_passThru->startMsgFilter(m_channelId, J2534::PassThru::PassFilter, + mask, pattern) != J2534::PassThru::NoError) + return false; + } + return true; +} + +bool PassThruCanIO::setConfigValue(J2534::Config::Parameter param, ulong value) +{ + const J2534::Config config {param, value}; + + return (m_passThru->setConfig(m_channelId, &config) == J2534::PassThru::NoError); +} + +void PassThruCanIO::pollForMessages() +{ + if (Q_UNLIKELY(!m_passThru)) { + qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Pass-thru interface not open"); + return; + } + const bool writePending = writeMessages(); + readMessages(writePending); +} + +bool PassThruCanIO::writeMessages() +{ + ulong numMsgs = m_ioBuffer.size(); + { + const QMutexLocker lock (&m_writeGuard); + numMsgs = qMin<ulong>(m_writeQueue.size(), numMsgs); + + for (ulong i = 0; i < numMsgs; ++i) { + const QCanBusFrame &frame = m_writeQueue.at(i); + J2534::Message &msg = m_ioBuffer[i]; + + const QByteArray payload = frame.payload(); + const ulong payloadSize = qMin<ulong>(payload.size(), + J2534::Message::maxSize - 4); + msg.setRxStatus({}); + msg.setTimestamp(0); + msg.setSize(4 + payloadSize); + msg.setExtraDataIndex(0); + + if (frame.hasExtendedFrameFormat()) + msg.setTxFlags(J2534::Message::OutCAN29BitID); + else + msg.setTxFlags({}); + + qToBigEndian<quint32>(frame.frameId(), msg.data()); + std::memcpy(msg.data() + 4, payload.data(), payloadSize); + } + } + if (numMsgs == 0) + return false; + + const auto status = m_passThru->writeMsgs(m_channelId, m_ioBuffer.constData(), + &numMsgs, pollTimeout); + if (status == J2534::PassThru::BufferFull) + return false; + + if (status != J2534::PassThru::NoError && status != J2534::PassThru::Timeout) { + emit errorOccurred(tr("Message write failed: %1").arg(m_passThru->lastErrorString()), + QCanBusDevice::WriteError); + return false; + } + if (numMsgs == 0) + return false; + + bool morePending; + { + const QMutexLocker lock (&m_writeGuard); + // De-queue successfully written frames. + m_writeQueue.erase(m_writeQueue.begin(), m_writeQueue.begin() + numMsgs); + morePending = !m_writeQueue.isEmpty(); + } + emit messagesSent(numMsgs); + + return morePending; +} + +void PassThruCanIO::readMessages(bool writePending) +{ + // If there are outgoing messages waiting to be written, just check + // for already received messages but do not block waiting for more. + const uint timeout = (writePending) ? 0 : pollTimeout; + + ulong numMsgs = m_ioBuffer.size(); + const auto status = m_passThru->readMsgs(m_channelId, m_ioBuffer.data(), + &numMsgs, timeout); + if (status == J2534::PassThru::BufferEmpty) + return; + + if (status != J2534::PassThru::NoError && status != J2534::PassThru::Timeout) { + emit errorOccurred(tr("Message read failed: %1").arg(m_passThru->lastErrorString()), + QCanBusDevice::ReadError); + if (status != J2534::PassThru::BufferOverflow) + return; + } + const int numFrames = qMin<ulong>(m_ioBuffer.size(), numMsgs); + QVector<QCanBusFrame> frames; + frames.reserve(numFrames); + + for (int i = 0; i < numFrames; ++i) { + const J2534::Message &msg = m_ioBuffer.at(i); + if (Q_UNLIKELY(msg.size() < 4) + || Q_UNLIKELY(msg.size() > J2534::Message::maxSize)) { + // This normally shouldn't happen, so a log message is appropriate. + qCWarning(QT_CANBUS_PLUGINS_PASSTHRU, + "Message with invalid size %lu received", msg.size()); + continue; + } + const quint32 msgId = qFromBigEndian<quint32>(msg.data()); + const QByteArray payload (msg.data() + 4, msg.size() - 4); + + QCanBusFrame frame (msgId, payload); + frame.setExtendedFrameFormat((msg.rxStatus() & J2534::Message::InCAN29BitID) != 0); + frame.setLocalEcho((msg.rxStatus() & J2534::Message::InTxMsgType) != 0); + frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(msg.timestamp())); + + frames.append(std::move(frame)); + } + if (!frames.isEmpty()) + emit messagesReceived(std::move(frames)); +} diff --git a/src/plugins/canbus/passthrucan/passthrucanio.h b/src/plugins/canbus/passthrucan/passthrucanio.h new file mode 100644 index 0000000..071174c --- /dev/null +++ b/src/plugins/canbus/passthrucan/passthrucanio.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef PASSTHRUCAN_PASSTHRUCANIO_H +#define PASSTHRUCAN_PASSTHRUCANIO_H + +#include "j2534passthru.h" + +#include <QtSerialBus/qcanbusdevice.h> +#include <QtSerialBus/qcanbusframe.h> +#include <QByteArray> +#include <QList> +#include <QMutex> +#include <QObject> +#include <QString> +#include <QTimer> +#include <QVariant> +#include <QVector> + +QT_BEGIN_NAMESPACE + +class PassThruCanIO : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(PassThruCanIO) +public: + static const uint pollTimeout = 100; // ms + + explicit PassThruCanIO(QObject *parent = nullptr); + virtual ~PassThruCanIO(); + + Q_INVOKABLE void open(const QString &library, const QByteArray &subDev, uint bitRate); + Q_INVOKABLE void close(); + Q_INVOKABLE void applyConfig(int key, const QVariant &value); + Q_INVOKABLE void listen(); + + // Internally locked; safe to call directly from any thread. + bool enqueueMessage(const QCanBusFrame &frame); + +Q_SIGNALS: + void errorOccurred(const QString &description, QCanBusDevice::CanBusError error); + void messagesReceived(QVector<QCanBusFrame> frames); + void messagesSent(qint64 count); + void openFinished(bool success); + void closeFinished(); + +private: + bool setMessageFilters(const QList<QCanBusDevice::Filter> &filters); + bool setConfigValue(J2534::Config::Parameter param, ulong value); + void pollForMessages(); + bool writeMessages(); + void readMessages(bool writePending); + + J2534::PassThru * m_passThru = nullptr; + J2534::PassThru::Handle m_deviceId = 0; + J2534::PassThru::Handle m_channelId = 0; + QTimer * m_idleNotifier = nullptr; + QVector<J2534::Message> m_ioBuffer; + QMutex m_writeGuard; + QList<QCanBusFrame> m_writeQueue; +}; + +QT_END_NAMESPACE + +#endif // PASSTHRUCAN_PASSTHRUCANIO_H diff --git a/src/plugins/canbus/passthrucan/plugin.json b/src/plugins/canbus/passthrucan/plugin.json new file mode 100644 index 0000000..2026547 --- /dev/null +++ b/src/plugins/canbus/passthrucan/plugin.json @@ -0,0 +1,3 @@ +{ + "Key": "passthrucan" +} |