diff options
Diffstat (limited to 'src/plugins/canbus/passthrucan/passthrucanio.cpp')
-rw-r--r-- | src/plugins/canbus/passthrucan/passthrucanio.cpp | 314 |
1 files changed, 314 insertions, 0 deletions
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)); +} |