diff options
Diffstat (limited to 'src/plugins/canbus')
18 files changed, 673 insertions, 53 deletions
diff --git a/src/plugins/canbus/peakcan/peakcan_symbols_p.h b/src/plugins/canbus/peakcan/peakcan_symbols_p.h index 9ab2e67..3470df2 100644 --- a/src/plugins/canbus/peakcan/peakcan_symbols_p.h +++ b/src/plugins/canbus/peakcan/peakcan_symbols_p.h @@ -267,21 +267,28 @@ #define PCAN_TYPE_DNG_SJA_EPP 0x06U // PCAN-Dongle EPP SJA1000 // Type definitions -#define TPCANHandle quint16 // Represents a PCAN hardware channel handle -#define TPCANStatus quint32 // Represents a PCAN status/error code -#define TPCANParameter quint8 // Represents a PCAN parameter to be read or set -#define TPCANDevice quint8 // Represents a PCAN device -#define TPCANMessageType quint8 // Represents the type of a PCAN message -#define TPCANType quint8 // Represents the type of PCAN hardware to be initialized -#define TPCANMode quint8 // Represents a PCAN filter mode -#define TPCANBaudrate quint16 // Represents a PCAN Baud rate register value -#define TPCANBitrateFD char * // Represents a PCAN-FD bit rate string -#define TPCANTimestampFD quint64 // Represents a timestamp of a received PCAN FD message +#ifdef Q_OS_MACOS +#define TPCANLong quint64 +#define TPCANLongToFrameID(a) static_cast<quint32>(a) +#else +#define TPCANLong quint32 +#define TPCANLongToFrameID(a) a +#endif +#define TPCANHandle quint16 // Represents a PCAN hardware channel handle +#define TPCANStatus TPCANLong // Represents a PCAN status/error code +#define TPCANParameter quint8 // Represents a PCAN parameter to be read or set +#define TPCANDevice quint8 // Represents a PCAN device +#define TPCANMessageType quint8 // Represents the type of a PCAN message +#define TPCANType quint8 // Represents the type of PCAN hardware to be initialized +#define TPCANMode quint8 // Represents a PCAN filter mode +#define TPCANBaudrate quint16 // Represents a PCAN Baud rate register value +#define TPCANBitrateFD char * // Represents a PCAN-FD bit rate string +#define TPCANTimestampFD quint64 // Represents a timestamp of a received PCAN FD message // Represents a PCAN message typedef struct tagTPCANMsg { - quint32 ID; // 11/29-bit message identifier + TPCANLong ID; // 11/29-bit message identifier TPCANMessageType MSGTYPE; // Type of the message quint8 LEN; // Data Length Code of the message (0..8) quint8 DATA[8]; // Data of the message (DATA[0]..DATA[7]) @@ -291,15 +298,15 @@ typedef struct tagTPCANMsg // Total Microseconds = micros + 1000 * millis + 0xFFFFFFFF * 1000 * millis_overflow typedef struct tagTPCANTimestamp { - quint32 millis; // Base-value: milliseconds: 0.. 2^32-1 - quint16 millis_overflow; // Roll-arounds of millis - quint16 micros; // Microseconds: 0..999 + TPCANLong millis; // Base-value: milliseconds: 0.. 2^32-1 + quint16 millis_overflow; // Roll-arounds of millis + quint16 micros; // Microseconds: 0..999 } TPCANTimestamp; // Represents a PCAN message from a FD capable hardware typedef struct tagTPCANMsgFD { - quint32 ID; // 11/29-bit message identifier + TPCANLong ID; // 11/29-bit message identifier TPCANMessageType MSGTYPE; // Type of the message quint8 DLC; // Data Length Code of the message (0..15) quint8 DATA[64]; // Data of the message (DATA[0]..DATA[63]) @@ -314,7 +321,7 @@ typedef struct tagTPCANMsgFD if (!symbolName) \ return false; -GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Initialize, TPCANHandle, TPCANBaudrate, TPCANType, quint32, quint16) +GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Initialize, TPCANHandle, TPCANBaudrate, TPCANType, TPCANLong, quint16) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_InitializeFD, TPCANHandle, TPCANBitrateFD) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Uninitialize, TPCANHandle) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Reset, TPCANHandle) @@ -323,15 +330,19 @@ GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Read, TPCANHandle, TPCANMsg *, TPCANTi GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_ReadFD, TPCANHandle, TPCANMsgFD *, TPCANTimestampFD *) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Write, TPCANHandle, TPCANMsg *) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_WriteFD, TPCANHandle, TPCANMsgFD *) -GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_FilterMessages, TPCANHandle, quint32, quint32, TPCANMode) -GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_GetValue, TPCANHandle, TPCANParameter, void *, quint32) -GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_SetValue, TPCANHandle, TPCANParameter, void *, quint32) +GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_FilterMessages, TPCANHandle, TPCANLong, TPCANLong, TPCANMode) +GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_GetValue, TPCANHandle, TPCANParameter, void *, TPCANLong) +GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_SetValue, TPCANHandle, TPCANParameter, void *, TPCANLong) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_GetErrorText, TPCANStatus, quint16, char *) inline bool resolveSymbols(QLibrary *pcanLibrary) { if (!pcanLibrary->isLoaded()) { - pcanLibrary->setFileName(QStringLiteral("pcanbasic")); + #ifdef Q_OS_MACOS + pcanLibrary->setFileName(QStringLiteral("PCBUSB")); + #else + pcanLibrary->setFileName(QStringLiteral("pcanbasic")); + #endif if (!pcanLibrary->load()) return false; } diff --git a/src/plugins/canbus/peakcan/peakcanbackend.cpp b/src/plugins/canbus/peakcan/peakcanbackend.cpp index f8932e9..b1a991d 100644 --- a/src/plugins/canbus/peakcan/peakcanbackend.cpp +++ b/src/plugins/canbus/peakcan/peakcanbackend.cpp @@ -68,6 +68,8 @@ bool PeakCanBackend::canCreate(QString *errorReason) #else static bool symbolsResolved = resolveSymbols(pcanLibrary()); if (Q_UNLIKELY(!symbolsResolved)) { + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot load library: %ls", + qUtf16Printable(pcanLibrary()->errorString())); *errorReason = pcanLibrary()->errorString(); return false; } @@ -331,15 +333,21 @@ bool PeakCanBackendPrivate::open() } if (Q_UNLIKELY(st != PCAN_ERROR_OK)) { - q->setError(systemErrorString(st), QCanBusDevice::ConnectionError); + const QString errorString = systemErrorString(st); + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot initialize hardware: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::ConnectionError); return false; } #if defined(Q_OS_WIN32) if (readHandle == INVALID_HANDLE_VALUE) { readHandle = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); - if (!readHandle) { - q->setError(qt_error_string(::GetLastError()), QCanBusDevice::ConnectionError); + if (Q_UNLIKELY(!readHandle)) { + const QString errorString = qt_error_string(::GetLastError()); + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot create receive event handler: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::ConnectionError); return false; } } @@ -353,7 +361,10 @@ bool PeakCanBackendPrivate::open() #else const TPCANStatus err = ::CAN_GetValue(channelIndex, PCAN_RECEIVE_EVENT, &readHandle, sizeof(readHandle)); if (Q_UNLIKELY(err != PCAN_ERROR_OK)) { - q->setError(systemErrorString(err), QCanBusDevice::ConnectionError); + const QString errorString = systemErrorString(err); + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot create receive event handler: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::ConnectionError); return false; } #endif @@ -380,8 +391,12 @@ void PeakCanBackendPrivate::close() quint32 value = 0; const TPCANStatus err = ::CAN_SetValue(channelIndex, PCAN_RECEIVE_EVENT, &value, sizeof(value)); - if (Q_UNLIKELY(err != PCAN_ERROR_OK)) - q->setError(systemErrorString(err), QCanBusDevice::ConnectionError); + if (Q_UNLIKELY(err != PCAN_ERROR_OK)) { + const QString errorString = systemErrorString(err); + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot unregister receive event handler: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::ConnectionError); + } const TPCANStatus st = ::CAN_Uninitialize(channelIndex); if (Q_UNLIKELY(st != PCAN_ERROR_OK)) @@ -389,8 +404,12 @@ void PeakCanBackendPrivate::close() #if defined(Q_OS_WIN32) if (readHandle && (readHandle != INVALID_HANDLE_VALUE)) { - if (Q_UNLIKELY(!::CloseHandle(readHandle))) - q->setError(qt_error_string(::GetLastError()), QCanBusDevice::ConnectionError); + const QString errorString = qt_error_string(::GetLastError()); + if (Q_UNLIKELY(!::CloseHandle(readHandle))) { + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot close read handle: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::ConnectionError); + } readHandle = INVALID_HANDLE_VALUE; } #else @@ -413,6 +432,7 @@ bool PeakCanBackendPrivate::setConfigurationParameter(int key, const QVariant &v case QCanBusDevice::DataBitRateKey: { const int dataBitrate = value.toInt(); if (Q_UNLIKELY(dataBitrateString(dataBitrate).isEmpty())) { + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Unsupported data bitrate value: %d", dataBitrate); q->setError(PeakCanBackend::tr("Unsupported data bitrate value: %1.").arg(dataBitrate), QCanBusDevice::ConfigurationError); return false; @@ -420,6 +440,7 @@ bool PeakCanBackendPrivate::setConfigurationParameter(int key, const QVariant &v return true; } default: + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Unsupported configuration key: %d", key); q->setError(PeakCanBackend::tr("Unsupported configuration key: %1").arg(key), QCanBusDevice::ConfigurationError); return false; @@ -558,8 +579,9 @@ void PeakCanBackendPrivate::startWrite() ::memcpy(message.DATA, payload.constData(), sizeof(message.DATA)); st = ::CAN_WriteFD(channelIndex, &message); } else if (frame.hasFlexibleDataRateFormat()) { - q->setError(PeakCanBackend::tr("Cannot send CAN FD frame format as CAN FD is not enabled."), - QCanBusDevice::WriteError); + const char errorString[] = "Cannot send CAN FD frame format as CAN FD is not enabled."; + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN(), errorString); + q->setError(PeakCanBackend::tr(errorString), QCanBusDevice::WriteError); } else { TPCANMsg message; ::memset(&message, 0, sizeof(message)); @@ -576,10 +598,14 @@ void PeakCanBackendPrivate::startWrite() st = ::CAN_Write(channelIndex, &message); } - if (Q_UNLIKELY(st != PCAN_ERROR_OK)) - q->setError(systemErrorString(st), QCanBusDevice::WriteError); - else + if (Q_UNLIKELY(st != PCAN_ERROR_OK)) { + const QString errorString = systemErrorString(st); + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot write frame: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::WriteError); + } else { emit q->framesWritten(qint64(1)); + } if (q->hasOutgoingFrames() && !writeNotifier->isActive()) writeNotifier->start(); @@ -611,7 +637,7 @@ void PeakCanBackendPrivate::startRead() continue; const int size = dlcToSize(static_cast<CanFrameDlc>(message.DLC)); - QCanBusFrame frame(message.ID, + QCanBusFrame frame(TPCANLongToFrameID(message.ID), QByteArray(reinterpret_cast<const char *>(message.DATA), size)); frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(static_cast<qint64>(timestamp))); frame.setExtendedFrameFormat(message.MSGTYPE & PCAN_MESSAGE_EXTENDED); @@ -641,7 +667,7 @@ void PeakCanBackendPrivate::startRead() continue; const int size = static_cast<int>(message.LEN); - QCanBusFrame frame(message.ID, + QCanBusFrame frame(TPCANLongToFrameID(message.ID), QByteArray(reinterpret_cast<const char *>(message.DATA), size)); const quint64 millis = timestamp.millis + Q_UINT64_C(0xFFFFFFFF) * timestamp.millis_overflow; const quint64 micros = Q_UINT64_C(1000) * millis + timestamp.micros; @@ -662,8 +688,9 @@ bool PeakCanBackendPrivate::verifyBitRate(int bitrate) Q_Q(PeakCanBackend); if (Q_UNLIKELY(isOpen)) { - q->setError(PeakCanBackend::tr("Cannot change bitrate for already opened device."), - QCanBusDevice::ConfigurationError); + const char errorString[] = "Cannot change bitrate for already opened device."; + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, errorString); + q->setError(PeakCanBackend::tr(errorString), QCanBusDevice::ConfigurationError); return false; } @@ -674,6 +701,7 @@ bool PeakCanBackendPrivate::verifyBitRate(int bitrate) isValidBitrate = bitrateCodeFromBitrate(bitrate) != PCAN_BAUD_INVALID; if (Q_UNLIKELY(!isValidBitrate)) { + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Unsupported bitrate value: %d.", bitrate); q->setError(PeakCanBackend::tr("Unsupported bitrate value: %1.").arg(bitrate), QCanBusDevice::ConfigurationError); } @@ -689,6 +717,12 @@ PeakCanBackend::PeakCanBackend(const QString &name, QObject *parent) d->setupChannel(name.toLatin1()); d->setupDefaultConfigurations(); + + std::function<void()> f = std::bind(&PeakCanBackend::resetController, this); + setResetControllerFunction(f); + + std::function<CanBusStatus()> g = std::bind(&PeakCanBackend::busStatus, this); + setCanBusStatusGetter(g); } PeakCanBackend::~PeakCanBackend() @@ -709,11 +743,11 @@ bool PeakCanBackend::open() if (Q_UNLIKELY(!d->open())) return false; - // apply all stored configurations except bitrate, because - // the bitrate can not be applied after opening of device + // Apply all stored configurations except bitrate, because + // the bitrate cannot be changed after opening the device const auto keys = configurationKeys(); for (int key : keys) { - if (key == QCanBusDevice::BitRateKey) + if (key == QCanBusDevice::BitRateKey || key == QCanBusDevice::DataBitRateKey) continue; const QVariant param = configurationParameter(key); const bool success = d->setConfigurationParameter(key, param); @@ -780,4 +814,29 @@ QString PeakCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame) return QString(); } +void PeakCanBackend::resetController() +{ + close(); + open(); +} + +QCanBusDevice::CanBusStatus PeakCanBackend::busStatus() const +{ + const TPCANStatus status = ::CAN_GetStatus(d_ptr->channelIndex); + + switch (status & PCAN_ERROR_ANYBUSERR) { + case PCAN_ERROR_OK: + return QCanBusDevice::CanBusStatus::Good; + case PCAN_ERROR_BUSWARNING: + return QCanBusDevice::CanBusStatus::Warning; + case PCAN_ERROR_BUSPASSIVE: + return QCanBusDevice::CanBusStatus::Error; + case PCAN_ERROR_BUSOFF: + return QCanBusDevice::CanBusStatus::BusOff; + default: + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Unknown CAN bus status: %lu.", ulong(status)); + return QCanBusDevice::CanBusStatus::Unknown; + } +} + QT_END_NAMESPACE diff --git a/src/plugins/canbus/peakcan/peakcanbackend.h b/src/plugins/canbus/peakcan/peakcanbackend.h index 7f7083e..a9e108e 100644 --- a/src/plugins/canbus/peakcan/peakcanbackend.h +++ b/src/plugins/canbus/peakcan/peakcanbackend.h @@ -72,6 +72,9 @@ public: static QList<QCanBusDeviceInfo> interfaces(); private: + void resetController(); + CanBusStatus busStatus() const; + PeakCanBackendPrivate * const d_ptr; }; diff --git a/src/plugins/canbus/socketcan/libsocketcan.cpp b/src/plugins/canbus/socketcan/libsocketcan.cpp new file mode 100644 index 0000000..c6144db --- /dev/null +++ b/src/plugins/canbus/socketcan/libsocketcan.cpp @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Andre Hartmann <aha_1980@gmx.de> +** 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 "libsocketcan.h" + +#include <QtCore/qloggingcategory.h> + +#if QT_CONFIG(library) +# include <QtCore/qlibrary.h> +#endif + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_SOCKETCAN) + +#define GENERATE_SYMBOL(returnType, symbolName, ...) \ + typedef returnType (*fp_##symbolName)(__VA_ARGS__); \ + static fp_##symbolName symbolName = nullptr; + +#define RESOLVE_SYMBOL(symbolName) \ + symbolName = reinterpret_cast<fp_##symbolName>(library->resolve(#symbolName)); \ + if (!symbolName) \ + return false; + +struct can_bittiming { + quint32 bitrate = 0; /* Bit-rate in bits/second */ + quint32 sample_point = 0; /* Sample point in one-tenth of a percent */ + quint32 tq = 0; /* Time quanta (TQ) in nanoseconds */ + quint32 prop_seg = 0; /* Propagation segment in TQs */ + quint32 phase_seg1 = 0; /* Phase buffer segment 1 in TQs */ + quint32 phase_seg2 = 0; /* Phase buffer segment 2 in TQs */ + quint32 sjw = 0; /* Synchronization jump width in TQs */ + quint32 brp = 0; /* Bit-rate prescaler */ +}; + +enum can_state { + CAN_STATE_ERROR_ACTIVE = 0, /* RX/TX error count < 96 */ + CAN_STATE_ERROR_WARNING, /* RX/TX error count < 128 */ + CAN_STATE_ERROR_PASSIVE, /* RX/TX error count < 256 */ + CAN_STATE_BUS_OFF, /* RX/TX error count >= 256 */ + CAN_STATE_STOPPED, /* Device is stopped */ + CAN_STATE_SLEEPING, /* Device is sleeping */ + CAN_STATE_MAX +}; + +GENERATE_SYMBOL(int, can_do_restart, const char * /* name */) +GENERATE_SYMBOL(int, can_do_stop, const char * /* name */) +GENERATE_SYMBOL(int, can_do_start, const char * /* name */) +GENERATE_SYMBOL(int, can_set_bitrate, const char * /* name */, quint32 /* bitrate */) +GENERATE_SYMBOL(int, can_get_bittiming, const char * /* name */, struct can_bittiming * /* bt */) +GENERATE_SYMBOL(int, can_get_state, const char * /* name */, int * /* state */) + +LibSocketCan::LibSocketCan(QString *errorString) +{ +#if QT_CONFIG(library) + auto resolveSymbols = [](QLibrary *library) { + if (!library->isLoaded()) { + library->setFileName(QStringLiteral("socketcan")); + if (!library->load()) + return false; + } + + RESOLVE_SYMBOL(can_do_start); + RESOLVE_SYMBOL(can_do_stop); + RESOLVE_SYMBOL(can_do_restart); + RESOLVE_SYMBOL(can_set_bitrate); + RESOLVE_SYMBOL(can_get_bittiming); + RESOLVE_SYMBOL(can_get_state); + + return true; + }; + + QLibrary lib; + if (Q_UNLIKELY(!resolveSymbols(&lib))) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls", qUtf16Printable(lib.errorString())); + if (errorString) + *errorString = lib.errorString(); + } +#else + const QString error = + QObject::tr("Cannot load library libsocketcan as Qt was built without QLibrary."); + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls", qUtf16Printable(error)); + if (errorString) + *errorString = error; +#endif +} + +/*! + Brings the CAN \a interface up. + + \internal + \note Requires appropriate permissions. +*/ +bool LibSocketCan::start(const QString &interface) +{ + if (!::can_do_start) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_do_start() is not available."); + return false; + } + + return ::can_do_start(interface.toLatin1().constData()) == 0; +} + +/*! + Brings the CAN \a interface down. + + \internal + \note Requires appropriate permissions. +*/ +bool LibSocketCan::stop(const QString &interface) +{ + if (!::can_do_stop) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_do_stop() is not available."); + return false; + } + + return ::can_do_stop(interface.toLatin1().constData()) == 0; +} + +/*! + Performs a CAN controller reset on the CAN \a interface. + + \internal + \note Reset can only be triggerd if the controller is in bus off + and the auto restart not turned on. + \note Requires appropriate permissions. + */ +bool LibSocketCan::restart(const QString &interface) +{ + if (!::can_do_restart) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_do_restart() is not available."); + return false; + } + + return ::can_do_restart(interface.toLatin1().constData()) == 0; +} + +/*! + Returns the configured bitrate for \a interface. + \internal +*/ +quint32 LibSocketCan::bitrate(const QString &interface) const +{ + if (!::can_get_bittiming) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_get_bittiming() is not available."); + return 0; + } + + struct can_bittiming bt; + if (::can_get_bittiming(interface.toLatin1().constData(), &bt) == 0) + return bt.bitrate; + + return 0; +} + +/*! + Sets the bitrate for the CAN \a interface. + + \internal + \note Requires appropriate permissions. + */ +bool LibSocketCan::setBitrate(const QString &interface, quint32 bitrate) +{ + if (!::can_set_bitrate) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_set_bitrate() is not available."); + return false; + } + + return ::can_set_bitrate(interface.toLatin1().constData(), bitrate) == 0; +} + +bool LibSocketCan::hasBusStatus() const +{ + return ::can_get_state != nullptr; +} + +QCanBusDevice::CanBusStatus LibSocketCan::busStatus(const QString &interface) const +{ + if (!::can_get_state) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_get_state() is not available."); + return QCanBusDevice::CanBusStatus::Unknown; + } + + int status = 0; + int result = ::can_get_state(interface.toLatin1().constData(), &status); + + if (result < 0) + return QCanBusDevice::CanBusStatus::Unknown; + + switch (status) { + case CAN_STATE_ERROR_ACTIVE: + return QCanBusDevice::CanBusStatus::Good; + case CAN_STATE_ERROR_WARNING: + return QCanBusDevice::CanBusStatus::Warning; + case CAN_STATE_ERROR_PASSIVE: + return QCanBusDevice::CanBusStatus::Error; + case CAN_STATE_BUS_OFF: + return QCanBusDevice::CanBusStatus::BusOff; + default: + // Device is stopped or sleeping, so status is unknown + return QCanBusDevice::CanBusStatus::Unknown; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/canbus/socketcan/libsocketcan.h b/src/plugins/canbus/socketcan/libsocketcan.h new file mode 100644 index 0000000..b77afa1 --- /dev/null +++ b/src/plugins/canbus/socketcan/libsocketcan.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Andre Hartmann <aha_1980@gmx.de> +** 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 LIBSOCKETCAN_H +#define LIBSOCKETCAN_H + +#include <QtCore/qglobal.h> +#include <QtSerialBus/qcanbusdevice.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QString; + +class LibSocketCan final +{ +public: + explicit LibSocketCan(QString *errorString = nullptr); + + bool start(const QString &interface); + bool stop(const QString &interface); + bool restart(const QString &interface); + + quint32 bitrate(const QString &interface) const; + bool setBitrate(const QString &interface, quint32 bitrate); + + bool hasBusStatus() const; + QCanBusDevice::CanBusStatus busStatus(const QString &interface) const; +}; + +QT_END_NAMESPACE + +#endif // LIBSOCKETCAN_H diff --git a/src/plugins/canbus/socketcan/socketcan.pro b/src/plugins/canbus/socketcan/socketcan.pro index 20a2c5d..cd731fd 100644 --- a/src/plugins/canbus/socketcan/socketcan.pro +++ b/src/plugins/canbus/socketcan/socketcan.pro @@ -3,9 +3,11 @@ TARGET = qtsocketcanbus QT = core serialbus HEADERS += \ + libsocketcan.h \ socketcanbackend.h SOURCES += \ + libsocketcan.cpp \ main.cpp \ socketcanbackend.cpp diff --git a/src/plugins/canbus/socketcan/socketcanbackend.cpp b/src/plugins/canbus/socketcan/socketcanbackend.cpp index a2da146..27fa326 100644 --- a/src/plugins/canbus/socketcan/socketcanbackend.cpp +++ b/src/plugins/canbus/socketcan/socketcanbackend.cpp @@ -36,6 +36,10 @@ #include "socketcanbackend.h" +#include "libsocketcan.h" + +#include <QtSerialBus/qcanbusdevice.h> + #include <QtCore/qdatastream.h> #include <QtCore/qdebug.h> #include <QtCore/qdiriterator.h> @@ -182,7 +186,25 @@ QList<QCanBusDeviceInfo> SocketCanBackend::interfaces() SocketCanBackend::SocketCanBackend(const QString &name) : canSocketName(name) { + QString errorString; + libSocketCan.reset(new LibSocketCan(&errorString)); + if (Q_UNLIKELY(!errorString.isEmpty())) { + qCInfo(QT_CANBUS_PLUGINS_SOCKETCAN, + "Cannot load library libsocketcan, some functionality will not be available.\n%ls", + qUtf16Printable(errorString)); + } + resetConfigurations(); + + std::function<void()> f = std::bind(&SocketCanBackend::resetController, this); + setResetControllerFunction(f); + + if (hasBusStatus()) { + // Only register busStatus when libsocketcan is available + // QCanBusDevice::hasBusStatus() will return false otherwise + std::function<CanBusStatus()> g = std::bind(&SocketCanBackend::busStatus, this); + setCanBusStatusGetter(g); + } } SocketCanBackend::~SocketCanBackend() @@ -201,6 +223,8 @@ void SocketCanBackend::resetConfigurations() QVariant::fromValue(QCanBusFrame::FrameErrors(QCanBusFrame::AnyError))); QCanBusDevice::setConfigurationParameter( QCanBusDevice::CanFdKey, false); + QCanBusDevice::setConfigurationParameter( + QCanBusDevice::BitRateKey, 500000); } bool SocketCanBackend::open() @@ -346,6 +370,12 @@ bool SocketCanBackend::applyConfigurationParameter(int key, const QVariant &valu success = true; break; } + case QCanBusDevice::BitRateKey: + { + const quint32 bitRate = value.toUInt(); + libSocketCan->setBitrate(canSocketName, bitRate); + break; + } default: setError(tr("SocketCanBackend: No such configuration as %1 in SocketCanBackend").arg(key), QCanBusDevice::CanBusError::ConfigurationError); @@ -731,4 +761,19 @@ void SocketCanBackend::readSocket() enqueueReceivedFrames(newFrames); } +void SocketCanBackend::resetController() +{ + libSocketCan->restart(canSocketName); +} + +bool SocketCanBackend::hasBusStatus() const +{ + return libSocketCan->hasBusStatus(); +} + +QCanBusDevice::CanBusStatus SocketCanBackend::busStatus() const +{ + return libSocketCan->busStatus(canSocketName); +} + QT_END_NAMESPACE diff --git a/src/plugins/canbus/socketcan/socketcanbackend.h b/src/plugins/canbus/socketcan/socketcanbackend.h index b49d267..199401e 100644 --- a/src/plugins/canbus/socketcan/socketcanbackend.h +++ b/src/plugins/canbus/socketcan/socketcanbackend.h @@ -52,8 +52,12 @@ #include <linux/can.h> #include <sys/time.h> +#include <memory> + QT_BEGIN_NAMESPACE +class LibSocketCan; + class SocketCanBackend : public QCanBusDevice { Q_OBJECT @@ -79,6 +83,9 @@ private: void resetConfigurations(); bool connectSocket(); bool applyConfigurationParameter(int key, const QVariant &value); + void resetController(); + bool hasBusStatus() const; + QCanBusDevice::CanBusStatus busStatus() const; canfd_frame m_frame; sockaddr_can m_address; @@ -89,6 +96,7 @@ private: qint64 canSocket = -1; QSocketNotifier *notifier = nullptr; + std::unique_ptr<LibSocketCan> libSocketCan; QString canSocketName; bool canFdOptionEnabled = false; }; diff --git a/src/plugins/canbus/systeccan/systeccan_symbols_p.h b/src/plugins/canbus/systeccan/systeccan_symbols_p.h index 102817c..ce32422 100644 --- a/src/plugins/canbus/systeccan/systeccan_symbols_p.h +++ b/src/plugins/canbus/systeccan/systeccan_symbols_p.h @@ -81,6 +81,20 @@ typedef void (DRV_CALLBACK_TYPE *tCallbackFktEx) (tUcanHandle handle, quint32 ev #define USBCAN_EVENT_FATALDISCON 8 // a USB-CANmodul has been disconnected during operation #define USBCAN_EVENT_RESERVED1 0x80 +// CAN status flags (is returned with function UcanGetStatus() or UcanGetStatusEx() ) +#define USBCAN_CANERR_OK 0x0000 // no error +#define USBCAN_CANERR_XMTFULL 0x0001 // Tx-buffer of the CAN controller is full +#define USBCAN_CANERR_OVERRUN 0x0002 // Rx-buffer of the CAN controller is full +#define USBCAN_CANERR_BUSLIGHT 0x0004 // Bus error: Error Limit 1 exceeded (refer to SJA1000 manual) +#define USBCAN_CANERR_BUSHEAVY 0x0008 // Bus error: Error Limit 2 exceeded (refer to SJA1000 manual) +#define USBCAN_CANERR_BUSOFF 0x0010 // Bus error: CAN controller has gone into Bus-Off state +#define USBCAN_CANERR_QRCVEMPTY 0x0020 // RcvQueue is empty +#define USBCAN_CANERR_QOVERRUN 0x0040 // RcvQueue overrun +#define USBCAN_CANERR_QXMTFULL 0x0080 // transmit queue is full +#define USBCAN_CANERR_REGTEST 0x0100 // Register test of the SJA1000 failed +#define USBCAN_CANERR_MEMTEST 0x0200 // Memory test failed +#define USBCAN_CANERR_TXMSGLOST 0x0400 // transmit CAN message was automatically deleted by firmware + #define kUcanModeNormal 0x00 // normal mode (send and receive) #define kUcanModeListenOnly 0x01 // listen only mode (only receive) #define kUcanModeTxEcho 0x02 // CAN messages which was sent will be received at UcanReadCanMsg.. @@ -88,8 +102,8 @@ typedef void (DRV_CALLBACK_TYPE *tCallbackFktEx) (tUcanHandle handle, quint32 ev #define kUcanModeHighResTimer 0x08 // high resolution time stamps in received CAN messages (only available with STM derivates) // ABR and ACR for mode "receive all CAN messages" -#define USBCAN_AMR_ALL (quint32) 0xffffffff -#define USBCAN_ACR_ALL (quint32) 0x00000000 +#define USBCAN_AMR_ALL 0xffffffffU +#define USBCAN_ACR_ALL 0x00000000U #define USBCAN_OCR_DEFAULT 0x1A // default OCR for standard GW-002 #define USBCAN_OCR_RS485_ISOLATED 0x1E // OCR for RS485 interface and galvanic isolation @@ -143,6 +157,11 @@ typedef struct _tCanMsgStruct { quint32 m_dwTime; // Time in ms } tCanMsgStruct; +typedef struct _tStatusStruct { + quint16 m_wCanStatus; // current CAN status + quint16 m_wUsbStatus; // current USB status +} tStatusStruct; + // Function return codes (encoding) #define USBCAN_SUCCESSFUL 0x00 // no error #define USBCAN_ERR 0x01 // error in library; function has not been executed @@ -280,7 +299,9 @@ GENERATE_SYMBOL_VARIABLE(UCANRET, UcanDeinitHardware, tUcanHandle) GENERATE_SYMBOL_VARIABLE(UCANRET, UcanInitCanEx2, tUcanHandle, quint8 /* channel */, tUcanInitCanParam *) GENERATE_SYMBOL_VARIABLE(UCANRET, UcanDeinitCanEx, tUcanHandle, quint8 /* channel */) GENERATE_SYMBOL_VARIABLE(UCANRET, UcanReadCanMsgEx, tUcanHandle, quint8 *, tCanMsgStruct *, quint32 *) +GENERATE_SYMBOL_VARIABLE(UCANRET, UcanResetCan, tUcanHandle) GENERATE_SYMBOL_VARIABLE(UCANRET, UcanWriteCanMsgEx, tUcanHandle, quint8, tCanMsgStruct *, quint32 *) +GENERATE_SYMBOL_VARIABLE(UCANRET, UcanGetStatus, tUcanHandle, tStatusStruct *) inline bool resolveSymbols(QLibrary *systecLibrary) { @@ -300,7 +321,9 @@ inline bool resolveSymbols(QLibrary *systecLibrary) RESOLVE_SYMBOL(UcanInitCanEx2); RESOLVE_SYMBOL(UcanDeinitCanEx); RESOLVE_SYMBOL(UcanReadCanMsgEx); + RESOLVE_SYMBOL(UcanResetCan); RESOLVE_SYMBOL(UcanWriteCanMsgEx); + RESOLVE_SYMBOL(UcanGetStatus); return true; } diff --git a/src/plugins/canbus/systeccan/systeccanbackend.cpp b/src/plugins/canbus/systeccan/systeccanbackend.cpp index 3fdd135..0663f39 100644 --- a/src/plugins/canbus/systeccan/systeccanbackend.cpp +++ b/src/plugins/canbus/systeccan/systeccanbackend.cpp @@ -114,7 +114,7 @@ QList<QCanBusDeviceInfo> SystecCanBackend::interfaces() { QList<QCanBusDeviceInfo> result; - ::UcanEnumerateHardware(&ucanEnumCallback, &result, false, 0, ~0, 0, ~0, 0, ~0); + ::UcanEnumerateHardware(&ucanEnumCallback, &result, false, 0, quint8(~0), 0, quint32(~0), 0, quint32(~0)); return result; } @@ -281,8 +281,8 @@ bool SystecCanBackendPrivate::setupChannel(const QString &interfaceName) const QRegularExpressionMatch match = re.match(interfaceName); if (Q_LIKELY(match.hasMatch())) { - device = match.captured(1).toInt(); - channel = match.captured(2).toInt(); + device = quint8(match.captured(1).toUShort()); + channel = quint8(match.captured(2).toUShort()); } else { q->setError(SystecCanBackend::tr("Invalid interface '%1'.") .arg(interfaceName), QCanBusDevice::ConnectionError); @@ -377,7 +377,7 @@ void SystecCanBackendPrivate::startWrite() ::memset(&message, 0, sizeof(message)); message.m_dwID = frame.frameId(); - message.m_bDLC = payload.size(); + message.m_bDLC = quint8(payload.size()); message.m_bFF = frame.hasExtendedFrameFormat() ? USBCAN_MSG_FF_EXT : USBCAN_MSG_FF_STD; @@ -454,6 +454,40 @@ bool SystecCanBackendPrivate::verifyBitRate(int bitrate) return true; } +void SystecCanBackendPrivate::resetController() +{ + ::UcanResetCan(handle); +} + +QCanBusDevice::CanBusStatus SystecCanBackendPrivate::busStatus() +{ + Q_Q(SystecCanBackend); + + tStatusStruct status; + ::memset(&status, 0, sizeof(status)); + const UCANRET result = ::UcanGetStatus(handle, &status); + + if (Q_UNLIKELY(result != USBCAN_SUCCESSFUL)) { + qCWarning(QT_CANBUS_PLUGINS_SYSTECCAN, "Can not query CAN bus status."); + q->setError(SystecCanBackend::tr("Can not query CAN bus status."), QCanBusDevice::ConfigurationError); + return QCanBusDevice::CanBusStatus::Unknown; + } + + if (status.m_wCanStatus & USBCAN_CANERR_BUSOFF) + return QCanBusDevice::CanBusStatus::BusOff; + + if (status.m_wCanStatus & USBCAN_CANERR_BUSHEAVY) + return QCanBusDevice::CanBusStatus::Error; + + if (status.m_wCanStatus & USBCAN_CANERR_BUSLIGHT) + return QCanBusDevice::CanBusStatus::Warning; + + if (status.m_wCanStatus == USBCAN_CANERR_OK) + return QCanBusDevice::CanBusStatus::Good; + + return QCanBusDevice::CanBusStatus::Unknown; +} + SystecCanBackend::SystecCanBackend(const QString &name, QObject *parent) : QCanBusDevice(parent), d_ptr(new SystecCanBackendPrivate(this)) @@ -462,6 +496,12 @@ SystecCanBackend::SystecCanBackend(const QString &name, QObject *parent) : d->setupChannel(name); d->setupDefaultConfigurations(); + + std::function<void()> f = std::bind(&SystecCanBackend::resetController, this); + setResetControllerFunction(f); + + std::function<CanBusStatus()> g = std::bind(&SystecCanBackend::busStatus, this); + setCanBusStatusGetter(g); } SystecCanBackend::~SystecCanBackend() @@ -553,4 +593,17 @@ QString SystecCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame) return QString(); } +void SystecCanBackend::resetController() +{ + Q_D(SystecCanBackend); + d->resetController(); +} + +QCanBusDevice::CanBusStatus SystecCanBackend::busStatus() +{ + Q_D(SystecCanBackend); + + return d->busStatus(); +} + QT_END_NAMESPACE diff --git a/src/plugins/canbus/systeccan/systeccanbackend.h b/src/plugins/canbus/systeccan/systeccanbackend.h index cb62808..22c1193 100644 --- a/src/plugins/canbus/systeccan/systeccanbackend.h +++ b/src/plugins/canbus/systeccan/systeccanbackend.h @@ -76,6 +76,9 @@ public: int channelNumber); private: + void resetController(); + QCanBusDevice::CanBusStatus busStatus(); + SystecCanBackendPrivate * const d_ptr; }; diff --git a/src/plugins/canbus/systeccan/systeccanbackend_p.h b/src/plugins/canbus/systeccan/systeccanbackend_p.h index 28eb7ff..b2da322 100644 --- a/src/plugins/canbus/systeccan/systeccanbackend_p.h +++ b/src/plugins/canbus/systeccan/systeccanbackend_p.h @@ -94,6 +94,8 @@ public: void startWrite(); void readAllReceivedMessages(); bool verifyBitRate(int bitrate); + void resetController(); + QCanBusDevice::CanBusStatus busStatus(); SystecCanBackend * const q_ptr; diff --git a/src/plugins/canbus/tinycan/tinycanbackend.cpp b/src/plugins/canbus/tinycan/tinycanbackend.cpp index 3891186..411b917 100644 --- a/src/plugins/canbus/tinycan/tinycanbackend.cpp +++ b/src/plugins/canbus/tinycan/tinycanbackend.cpp @@ -405,8 +405,7 @@ void TinyCanBackendPrivate::startRead() } else { if (status.CanStatus == CAN_STATUS_BUS_OFF) { qCWarning(QT_CANBUS_PLUGINS_TINYCAN, "CAN bus is in off state, trying to reset the bus."); - if (::CanSetMode(channelIndex, OP_CAN_RESET, CAN_CMD_NONE) < 0) - q->setError(systemErrorString(ret), QCanBusDevice::CanBusError::ReadError); + resetController(); } } @@ -446,7 +445,8 @@ void TinyCanBackendPrivate::startupDriver() ::CanSetEvents(EVENT_ENABLE_RX_MESSAGES); } else if (Q_UNLIKELY(driverRefCount < 0)) { - qCritical("Wrong reference counter: %d", driverRefCount); + qCCritical(QT_CANBUS_PLUGINS_TINYCAN, "Wrong driver reference counter: %d", + driverRefCount); return; } @@ -458,7 +458,8 @@ void TinyCanBackendPrivate::cleanupDriver() --driverRefCount; if (Q_UNLIKELY(driverRefCount < 0)) { - qCritical("Wrong reference counter: %d", driverRefCount); + qCCritical(QT_CANBUS_PLUGINS_TINYCAN, "Wrong driver reference counter: %d", + driverRefCount); driverRefCount = 0; } else if (driverRefCount == 0) { ::CanSetEvents(EVENT_DISABLE_ALL); @@ -466,6 +467,18 @@ void TinyCanBackendPrivate::cleanupDriver() } } +void TinyCanBackendPrivate::resetController() +{ + Q_Q(TinyCanBackend); + qint32 ret = ::CanSetMode(channelIndex, OP_CAN_RESET, CAN_CMD_NONE); + if (Q_UNLIKELY(ret < 0)) { + const QString errorString = systemErrorString(ret); + qCWarning(QT_CANBUS_PLUGINS_TINYCAN, "Cannot perform hardware reset: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::CanBusError::ConfigurationError); + } +} + bool TinyCanBackendPrivate::setBitRate(int bitrate) { Q_Q(TinyCanBackend); @@ -496,6 +509,9 @@ TinyCanBackend::TinyCanBackend(const QString &name, QObject *parent) d->setupChannel(name); d->setupDefaultConfigurations(); + + std::function<void()> f = std::bind(&TinyCanBackend::resetController, this); + setResetControllerFunction(f); } TinyCanBackend::~TinyCanBackend() @@ -589,4 +605,10 @@ QString TinyCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame) return QString(); } +void TinyCanBackend::resetController() +{ + Q_D(TinyCanBackend); + d->resetController(); +} + QT_END_NAMESPACE diff --git a/src/plugins/canbus/tinycan/tinycanbackend.h b/src/plugins/canbus/tinycan/tinycanbackend.h index 5f504ca..428e9bc 100644 --- a/src/plugins/canbus/tinycan/tinycanbackend.h +++ b/src/plugins/canbus/tinycan/tinycanbackend.h @@ -72,6 +72,8 @@ public: static QList<QCanBusDeviceInfo> interfaces(); private: + void resetController(); + TinyCanBackendPrivate * const d_ptr; }; diff --git a/src/plugins/canbus/tinycan/tinycanbackend_p.h b/src/plugins/canbus/tinycan/tinycanbackend_p.h index 905175c..25316a2 100644 --- a/src/plugins/canbus/tinycan/tinycanbackend_p.h +++ b/src/plugins/canbus/tinycan/tinycanbackend_p.h @@ -75,6 +75,7 @@ public: void startRead(); void startupDriver(); void cleanupDriver(); + void resetController(); bool setBitRate(int bitrate); diff --git a/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h b/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h index 3f2501f..8c1ad52 100644 --- a/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h +++ b/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h @@ -249,9 +249,27 @@ static_assert(sizeof(s_xl_can_msg) == 32, "Invalid size of s_xl_can_msg structur #define XL_TRANSCEIVER_EVENT_ERROR 1 #define XL_TRANSCEIVER_EVENT_CHANGED 2 +#define XL_CHIPSTAT_BUSOFF 0x01 +#define XL_CHIPSTAT_ERROR_PASSIVE 0x02 +#define XL_CHIPSTAT_ERROR_WARNING 0x04 +#define XL_CHIPSTAT_ERROR_ACTIVE 0x08 + +#define XL_CAN_STATE_FLAG_SJA_MODE 0x00000001 + +// CAN Chip status +struct s_xl_chip_state { + unsigned char busStatus; + unsigned char txErrorCounter; + unsigned char rxErrorCounter; + unsigned char chipState; // raw Status Register Value + unsigned int flags; +}; +static_assert(sizeof(s_xl_chip_state) == 8, "Invalid size of s_xl_chip_state structure"); + // basic bus message structure union s_xl_tag_data { struct s_xl_can_msg msg; + struct s_xl_chip_state chipState; }; // event type definition (48 bytes) @@ -456,6 +474,7 @@ GENERATE_SYMBOL_VARIABLE(XLstatus, xlCanSetChannelBitrate, XLportHandle, XLacces GENERATE_SYMBOL_VARIABLE(XLstatus, xlCanTransmit, XLportHandle, XLaccess, quint32 *, void *) GENERATE_SYMBOL_VARIABLE(XLstatus, xlReceive, XLportHandle, quint32 *, XLevent *) GENERATE_SYMBOL_VARIABLE(XLstatus, xlSetNotification, XLportHandle, XLhandle *, int) +GENERATE_SYMBOL_VARIABLE(XLstatus, xlCanRequestChipState, XLportHandle, XLaccess) GENERATE_SYMBOL_VARIABLE(char *, xlGetErrorString, XLstatus) inline bool resolveSymbols(QLibrary *vectorcanLibrary) @@ -481,6 +500,7 @@ inline bool resolveSymbols(QLibrary *vectorcanLibrary) RESOLVE_SYMBOL(xlCanTransmit) RESOLVE_SYMBOL(xlReceive) RESOLVE_SYMBOL(xlSetNotification) + RESOLVE_SYMBOL(xlCanRequestChipState) RESOLVE_SYMBOL(xlGetErrorString) return true; diff --git a/src/plugins/canbus/vectorcan/vectorcanbackend.cpp b/src/plugins/canbus/vectorcan/vectorcanbackend.cpp index 9026888..9d3bdef 100644 --- a/src/plugins/canbus/vectorcan/vectorcanbackend.cpp +++ b/src/plugins/canbus/vectorcan/vectorcanbackend.cpp @@ -271,7 +271,8 @@ void VectorCanBackendPrivate::setupChannel(const QString &interfaceName) } } - qCritical("Unable to parse the channel %ls", qUtf16Printable(interfaceName)); + qCCritical(QT_CANBUS_PLUGINS_VECTORCAN, "Unable to parse the channel %ls", + qUtf16Printable(interfaceName)); } void VectorCanBackendPrivate::setupDefaultConfigurations() @@ -388,7 +389,8 @@ XLstatus VectorCanBackendPrivate::loadDriver() return status; } else if (Q_UNLIKELY(driverRefCount < 0)) { - qCritical("Wrong reference counter: %d", driverRefCount); + qCCritical(QT_CANBUS_PLUGINS_VECTORCAN, "Wrong driver reference counter: %d", + driverRefCount); return XL_ERR_CANNOT_OPEN_DRIVER; } @@ -412,7 +414,8 @@ void VectorCanBackendPrivate::cleanupDriver() --driverRefCount; if (Q_UNLIKELY(driverRefCount < 0)) { - qCritical("Wrong reference counter: %d", driverRefCount); + qCCritical(QT_CANBUS_PLUGINS_VECTORCAN, "Wrong driver reference counter: %d", + driverRefCount); driverRefCount = 0; } else if (driverRefCount == 0) { ::xlCloseDriver(); @@ -443,6 +446,9 @@ VectorCanBackend::VectorCanBackend(const QString &name, QObject *parent) d->setupChannel(name); d->setupDefaultConfigurations(); + + std::function<CanBusStatus()> g = std::bind(&VectorCanBackend::busStatus, this); + setCanBusStatusGetter(g); } VectorCanBackend::~VectorCanBackend() @@ -537,4 +543,48 @@ QString VectorCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame) return QString(); } +QCanBusDevice::CanBusStatus VectorCanBackend::busStatus() +{ + Q_D(VectorCanBackend); + + const XLstatus requestStatus = ::xlCanRequestChipState(d->portHandle, d->channelMask); + if (Q_UNLIKELY(requestStatus != XL_SUCCESS)) { + const QString errorString = d->systemErrorString(requestStatus); + qCWarning(QT_CANBUS_PLUGINS_VECTORCAN, "Can not query CAN bus status: %ls.", + qUtf16Printable(errorString)); + setError(errorString, QCanBusDevice::CanBusError::ReadError); + return QCanBusDevice::CanBusStatus::Unknown; + } + + quint32 eventCount = 1; + XLevent event; + ::memset(&event, 0, sizeof(event)); + + const XLstatus receiveStatus = ::xlReceive(d->portHandle, &eventCount, &event); + if (Q_UNLIKELY(receiveStatus != XL_SUCCESS)) { + const QString errorString = d->systemErrorString(receiveStatus); + qCWarning(QT_CANBUS_PLUGINS_VECTORCAN, "Can not query CAN bus status: %ls.", + qUtf16Printable(errorString)); + setError(errorString, QCanBusDevice::CanBusError::ReadError); + return QCanBusDevice::CanBusStatus::Unknown; + } + + if (Q_LIKELY(event.tag == XL_CHIP_STATE)) { + switch (event.tagData.chipState.busStatus) { + case XL_CHIPSTAT_BUSOFF: + return QCanBusDevice::CanBusStatus::BusOff; + case XL_CHIPSTAT_ERROR_PASSIVE: + return QCanBusDevice::CanBusStatus::Error; + case XL_CHIPSTAT_ERROR_WARNING: + return QCanBusDevice::CanBusStatus::Warning; + case XL_CHIPSTAT_ERROR_ACTIVE: + return QCanBusDevice::CanBusStatus::Good; + } + } + + qCWarning(QT_CANBUS_PLUGINS_VECTORCAN, "Unknown CAN bus status: %u", + uint(event.tagData.chipState.busStatus)); + return QCanBusDevice::CanBusStatus::Unknown; +} + QT_END_NAMESPACE diff --git a/src/plugins/canbus/vectorcan/vectorcanbackend.h b/src/plugins/canbus/vectorcan/vectorcanbackend.h index 165d369..89b9da6 100644 --- a/src/plugins/canbus/vectorcan/vectorcanbackend.h +++ b/src/plugins/canbus/vectorcan/vectorcanbackend.h @@ -71,6 +71,8 @@ public: static QList<QCanBusDeviceInfo> interfaces(); private: + QCanBusDevice::CanBusStatus busStatus(); + VectorCanBackendPrivate * const d_ptr; }; |