// Copyright (C) 2017 Denis Shienkov // Copyright (c) 2020 Andre Hartmann // Copyright (C) 2017 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "peakcanbackend.h" #include "peakcanbackend_p.h" #include "peakcan_symbols_p.h" #include #include #include #include #include #include #ifdef Q_OS_WIN32 # include #else # include #endif QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_PEAKCAN) #ifndef LINK_LIBPCANBASIC Q_GLOBAL_STATIC(QLibrary, pcanLibrary) #endif bool PeakCanBackend::canCreate(QString *errorReason) { #ifdef LINK_LIBPCANBASIC Q_UNUSED(errorReason); #else static bool symbolsResolved = resolvePeakCanSymbols(pcanLibrary()); if (Q_UNLIKELY(!symbolsResolved)) { qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot load library: %ls", qUtf16Printable(pcanLibrary()->errorString())); *errorReason = pcanLibrary()->errorString(); return false; } #endif char apiVersion[32]; TPCANStatus stat = CAN_GetValue(PCAN_NONEBUS, PCAN_API_VERSION, apiVersion, sizeof(apiVersion)); if (Q_UNLIKELY(stat != PCAN_ERROR_OK)) { qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot resolve PCAN-API version!"); return false; } qCInfo(QT_CANBUS_PLUGINS_PEAKCAN, "Using PCAN-API version: %s", apiVersion); return true; } struct PcanChannel{ char name[6]; TPCANHandle index; }; static const PcanChannel pcanChannels[] = { { "usb0", PCAN_USBBUS1 }, { "usb1", PCAN_USBBUS2 }, { "usb2", PCAN_USBBUS3 }, { "usb3", PCAN_USBBUS4 }, { "usb4", PCAN_USBBUS5 }, { "usb5", PCAN_USBBUS6 }, { "usb6", PCAN_USBBUS7 }, { "usb7", PCAN_USBBUS8 }, { "usb8", PCAN_USBBUS9 }, { "usb9", PCAN_USBBUS10 }, { "usb10", PCAN_USBBUS11 }, { "usb11", PCAN_USBBUS12 }, { "usb12", PCAN_USBBUS13 }, { "usb13", PCAN_USBBUS14 }, { "usb14", PCAN_USBBUS15 }, { "usb15", PCAN_USBBUS16 }, { "pci0", PCAN_PCIBUS1 }, { "pci1", PCAN_PCIBUS2 }, { "pci2", PCAN_PCIBUS3 }, { "pci3", PCAN_PCIBUS4 }, { "pci4", PCAN_PCIBUS5 }, { "pci5", PCAN_PCIBUS6 }, { "pci6", PCAN_PCIBUS7 }, { "pci7", PCAN_PCIBUS8 }, { "pci8", PCAN_PCIBUS9 }, { "pci9", PCAN_PCIBUS10 }, { "pci10", PCAN_PCIBUS11 }, { "pci11", PCAN_PCIBUS12 }, { "pci12", PCAN_PCIBUS13 }, { "pci13", PCAN_PCIBUS14 }, { "pci14", PCAN_PCIBUS15 }, { "pci15", PCAN_PCIBUS16 }, { "none", PCAN_NONEBUS } }; QList PeakCanBackend::interfacesByChannelCondition(Availability available) { QList result; for (int i = 0; pcanChannels[i].index != PCAN_NONEBUS; ++i) { uint value = 0; const TPCANHandle index = pcanChannels[i].index; const TPCANStatus stat = ::CAN_GetValue(index, PCAN_CHANNEL_CONDITION, &value, sizeof(value)); if ((stat == PCAN_ERROR_OK) && (value & uint(available))) { const TPCANStatus fdStat = ::CAN_GetValue(index, PCAN_CHANNEL_FEATURES, &value, sizeof(value)); const bool isFd = (fdStat == PCAN_ERROR_OK) && (value & FEATURE_FD_CAPABLE); char description[256] = {0}; const TPCANStatus descStat = ::CAN_GetValue(index, PCAN_HARDWARE_NAME, description, sizeof(description)); if (descStat != PCAN_ERROR_OK) description[0] = 0; int channel = 0; const TPCANStatus chnStat = ::CAN_GetValue(index, PCAN_CONTROLLER_NUMBER, &channel, sizeof(channel)); if (chnStat != PCAN_ERROR_OK) channel = 0; QString alias; quint32 deviceId = 0; const TPCANStatus idStat = ::CAN_GetValue(index, PCAN_DEVICE_ID, &deviceId, sizeof(deviceId)); if (idStat == PCAN_ERROR_OK) alias = QString::number(deviceId); result.append(QCanBusDevice::createDeviceInfo(QStringLiteral("peakcan"), QLatin1String(pcanChannels[i].name), QString(), QLatin1String(description), alias, channel, false, isFd)); } } return result; } static QString pcanChannelNameForIndex(uint index) { const auto pcanChannel = std::find_if(std::begin(pcanChannels), std::end(pcanChannels), [index](PcanChannel channel) { return channel.index == index; }); if (Q_LIKELY(pcanChannel != std::end(pcanChannels))) return pcanChannel->name; qWarning("%s: Cannot get channel name for index %u.", Q_FUNC_INFO, index); return QStringLiteral("none"); } QList PeakCanBackend::interfacesByAttachedChannels(Availability available, bool *ok) { *ok = true; quint32 count = 0; const TPCANStatus countStat = ::CAN_GetValue(0, PCAN_ATTACHED_CHANNELS_COUNT, &count, sizeof(count)); if (Q_UNLIKELY(countStat != PCAN_ERROR_OK)) { qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot query PCAN_ATTACHED_CHANNELS_COUNT."); *ok = false; return {}; } if (count == 0) return {}; std::vector infos(count); const TPCANStatus infosStat = ::CAN_GetValue(0, PCAN_ATTACHED_CHANNELS, infos.data(), quint32(infos.size() * sizeof(TPCANChannelInformation))); if (Q_UNLIKELY(infosStat != PCAN_ERROR_OK)) { qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot query PCAN_ATTACHED_CHANNELS."); *ok = false; return {}; } QList result; for (quint32 i = 0; i < count; ++i) { auto info = infos[i]; if (info.channel_condition & uint(available)) { const QString name = pcanChannelNameForIndex(info.channel_handle); const QString description = info.device_name; const QString alias = QString::number(info.device_id); const int channel = info.controller_number; const bool isCanFd = (info.device_features & FEATURE_FD_CAPABLE); result.append(createDeviceInfo(QStringLiteral("peakcan"), name, QString(), description, alias, channel, false, isCanFd)); } } return result; } QList PeakCanBackend::attachedInterfaces(Availability available) { #ifdef Q_OS_WIN bool ok = false; QList attachedChannelsResult = interfacesByAttachedChannels(available, &ok); if (ok) return attachedChannelsResult; #endif QList result = interfacesByChannelCondition(available); return result; } QList PeakCanBackend::interfaces() { return attachedInterfaces(Availability::Available); } QCanBusDeviceInfo PeakCanBackend::deviceInfo() const { const uint index = d_ptr->channelIndex; const QString name = pcanChannelNameForIndex(index); const QList availableDevices = attachedInterfaces(Availability::Occupied); const auto deviceInfo = std::find_if(availableDevices.constBegin(), availableDevices.constEnd(), [name](const QCanBusDeviceInfo &info) { return name == info.name(); }); if (Q_LIKELY(deviceInfo != availableDevices.constEnd())) return *deviceInfo; qWarning("%s: Cannot get device info for index %u.", Q_FUNC_INFO, index); return QCanBusDevice::deviceInfo(); } #if defined(Q_OS_WIN32) class PeakCanReadNotifier : public QWinEventNotifier { // no Q_OBJECT macro! public: explicit PeakCanReadNotifier(PeakCanBackendPrivate *d, QObject *parent) : QWinEventNotifier(parent) , dptr(d) { setHandle(dptr->readHandle); connect(this, &QWinEventNotifier::activated, this, [this]() { dptr->startRead(); }); } private: PeakCanBackendPrivate * const dptr; }; #else class PeakCanReadNotifier : public QSocketNotifier { // no Q_OBJECT macro! public: explicit PeakCanReadNotifier(PeakCanBackendPrivate *d, QObject *parent) : QSocketNotifier(d->readHandle, QSocketNotifier::Read, parent) , dptr(d) { } protected: bool event(QEvent *e) override { if (e->type() == QEvent::SockAct) { dptr->startRead(); return true; } return QSocketNotifier::event(e); } private: PeakCanBackendPrivate * const dptr; }; #endif class PeakCanWriteNotifier : public QTimer { // no Q_OBJECT macro! public: PeakCanWriteNotifier(PeakCanBackendPrivate *d, QObject *parent) : QTimer(parent) , dptr(d) { } protected: void timerEvent(QTimerEvent *e) override { if (e->timerId() == timerId()) { dptr->startWrite(); return; } QTimer::timerEvent(e); } private: PeakCanBackendPrivate * const dptr; }; PeakCanBackendPrivate::PeakCanBackendPrivate(PeakCanBackend *q) : q_ptr(q) { } struct BitrateItem { int bitrate; TPCANBaudrate code; }; struct BitrateLessFunctor { bool operator()( const BitrateItem &item1, const BitrateItem &item2) const { return item1.bitrate < item2.bitrate; } }; static TPCANBaudrate bitrateCodeFromBitrate(int bitrate) { static const BitrateItem bitratetable[] = { { 5000, PCAN_BAUD_5K }, { 10000, PCAN_BAUD_10K }, { 20000, PCAN_BAUD_20K }, { 33000, PCAN_BAUD_33K }, { 47000, PCAN_BAUD_47K }, { 50000, PCAN_BAUD_50K }, { 83000, PCAN_BAUD_83K }, { 95000, PCAN_BAUD_95K }, { 100000, PCAN_BAUD_100K }, { 125000, PCAN_BAUD_125K }, { 250000, PCAN_BAUD_250K }, { 500000, PCAN_BAUD_500K }, { 800000, PCAN_BAUD_800K }, { 1000000, PCAN_BAUD_1M } }; static const BitrateItem *endtable = bitratetable + (sizeof(bitratetable) / sizeof(*bitratetable)); const BitrateItem item = { bitrate , 0 }; const BitrateItem *where = std::lower_bound(bitratetable, endtable, item, BitrateLessFunctor()); return where != endtable ? where->code : PCAN_BAUD_INVALID; } static QByteArray nominalBitrateString(int nominalBitrate) { switch (nominalBitrate) { case 125000: return "f_clock=80000000, nom_brp=40, nom_tseg1=12, nom_tseg2=3, nom_sjw=1"; case 250000: return "f_clock=80000000, nom_brp=20, nom_tseg1=12, nom_tseg2=3, nom_sjw=1"; case 500000: return "f_clock=80000000, nom_brp=10, nom_tseg1=12, nom_tseg2=3, nom_sjw=1"; case 1000000: return "f_clock=80000000, nom_brp=10, nom_tseg1=5, nom_tseg2=2, nom_sjw=1"; default: return QByteArray(); } } static QByteArray dataBitrateString(int dataBitrate) { switch (dataBitrate) { case 2000000: return ", data_brp=4, data_tseg1=7, data_tseg2=2, data_sjw=1"; case 4000000: return ", data_brp=2, data_tseg1=7, data_tseg2=2, data_sjw=1"; case 8000000: return ", data_brp=1, data_tseg1=7, data_tseg2=2, data_sjw=1"; case 10000000: return ", data_brp=1, data_tseg1=5, data_tseg2=2, data_sjw=1"; default: return QByteArray(); } } static QByteArray bitrateStringFromBitrate(int nominalBitrate, int dataBitrate) { QByteArray result = nominalBitrateString(nominalBitrate); if (result.isEmpty()) return QByteArray(); result += dataBitrateString(dataBitrate); return result; } bool PeakCanBackendPrivate::open() { Q_Q(PeakCanBackend); const int nominalBitrate = q->configurationParameter(QCanBusDevice::BitRateKey).toInt(); TPCANStatus st = PCAN_ERROR_OK; if (isFlexibleDatarateEnabled) { const int dataBitrate = q->configurationParameter(QCanBusDevice::DataBitRateKey).toInt(); const QByteArray bitrateStr = bitrateStringFromBitrate(nominalBitrate, dataBitrate); st = ::CAN_InitializeFD(channelIndex, const_cast(bitrateStr.data())); } else { const TPCANBaudrate bitrateCode = bitrateCodeFromBitrate(nominalBitrate); st = ::CAN_Initialize(channelIndex, bitrateCode, 0, 0, 0); } if (Q_UNLIKELY(st != PCAN_ERROR_OK)) { 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 (Q_UNLIKELY(!readHandle)) { const QString errorString = qt_error_string(int(::GetLastError())); qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot create receive event handler: %ls", qUtf16Printable(errorString)); q->setError(errorString, QCanBusDevice::ConnectionError); return false; } } const TPCANStatus err = ::CAN_SetValue(channelIndex, PCAN_RECEIVE_EVENT, &readHandle, sizeof(readHandle)); if (Q_UNLIKELY(err != PCAN_ERROR_OK)) { q->setError(systemErrorString(err), QCanBusDevice::ConnectionError); return false; } #else const TPCANStatus err = ::CAN_GetValue(channelIndex, PCAN_RECEIVE_EVENT, &readHandle, sizeof(readHandle)); if (Q_UNLIKELY(err != PCAN_ERROR_OK)) { 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 writeNotifier = new PeakCanWriteNotifier(this, q); writeNotifier->setInterval(0); readNotifier = new PeakCanReadNotifier(this, q); readNotifier->setEnabled(true); isOpen = true; return true; } void PeakCanBackendPrivate::close() { Q_Q(PeakCanBackend); delete readNotifier; readNotifier = nullptr; delete writeNotifier; writeNotifier = nullptr; quint32 value = 0; const TPCANStatus err = ::CAN_SetValue(channelIndex, PCAN_RECEIVE_EVENT, &value, sizeof(value)); 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)) q->setError(systemErrorString(st), QCanBusDevice::ConnectionError); #if defined(Q_OS_WIN32) if (readHandle && (readHandle != INVALID_HANDLE_VALUE)) { const QString errorString = qt_error_string(int(::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 readHandle = -1; #endif isOpen = false; } bool PeakCanBackendPrivate::setConfigurationParameter(QCanBusDevice::ConfigurationKey key, const QVariant &value) { Q_Q(PeakCanBackend); switch (key) { case QCanBusDevice::BitRateKey: return verifyBitRate(value.toInt()); case QCanBusDevice::CanFdKey: isFlexibleDatarateEnabled = value.toBool(); return true; 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; } 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; } } void PeakCanBackendPrivate::setupChannel(const QByteArray &interfaceName) { const PcanChannel *chn = pcanChannels; while (chn->index != PCAN_NONEBUS && chn->name != interfaceName) ++chn; channelIndex = chn->index; } // Calls only when the device is closed void PeakCanBackendPrivate::setupDefaultConfigurations() { Q_Q(PeakCanBackend); q->setConfigurationParameter(QCanBusDevice::BitRateKey, 500000); } QString PeakCanBackendPrivate::systemErrorString(TPCANStatus errorCode) { QByteArray buffer(256, 0); if (Q_UNLIKELY(::CAN_GetErrorText(errorCode, 0, buffer.data()) != PCAN_ERROR_OK)) return PeakCanBackend::tr("Unable to retrieve an error string"); return QString::fromLatin1(buffer); } enum CanFrameDlc { Dlc00 = 0, Dlc01 = 1, Dlc02 = 2, Dlc03 = 3, Dlc04 = 4, Dlc05 = 5, Dlc06 = 6, Dlc07 = 7, Dlc08 = 8, Dlc12 = 9, Dlc16 = 10, Dlc20 = 11, Dlc24 = 12, Dlc32 = 13, Dlc48 = 14, Dlc64 = 15 }; static CanFrameDlc sizeToDlc(int size) { switch (size) { case 12: return Dlc12; case 16: return Dlc16; case 20: return Dlc20; case 24: return Dlc24; case 32: return Dlc32; case 48: return Dlc48; case 64: return Dlc64; default: if (size >= 0 && size <= 8) return static_cast(size); return Dlc00; } } static int dlcToSize(CanFrameDlc dlc) { switch (dlc) { case Dlc00: case Dlc01: case Dlc02: case Dlc03: case Dlc04: case Dlc05: case Dlc06: case Dlc07: case Dlc08: return static_cast(dlc); case Dlc12: return 12; case Dlc16: return 16; case Dlc20: return 20; case Dlc24: return 24; case Dlc32: return 32; case Dlc48: return 48; case Dlc64: return 64; } return 0; } void PeakCanBackendPrivate::startWrite() { Q_Q(PeakCanBackend); if (!q->hasOutgoingFrames()) { writeNotifier->stop(); return; } const QCanBusFrame frame = q->dequeueOutgoingFrame(); const QByteArray payload = frame.payload(); const qsizetype payloadSize = payload.size(); TPCANStatus st = PCAN_ERROR_OK; if (isFlexibleDatarateEnabled) { TPCANMsgFD message = {}; message.ID = frame.frameId(); message.DLC = sizeToDlc(payloadSize); message.MSGTYPE = frame.hasExtendedFrameFormat() ? PCAN_MESSAGE_EXTENDED : PCAN_MESSAGE_STANDARD; if (frame.hasFlexibleDataRateFormat()) message.MSGTYPE |= PCAN_MESSAGE_FD; if (frame.hasBitrateSwitch()) message.MSGTYPE |= PCAN_MESSAGE_BRS; if (frame.frameType() == QCanBusFrame::RemoteRequestFrame) message.MSGTYPE |= PCAN_MESSAGE_RTR; // we do not care about the payload else ::memcpy(message.DATA, payload.constData(), payloadSize); st = ::CAN_WriteFD(channelIndex, &message); } else if (frame.hasFlexibleDataRateFormat()) { 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 = {}; message.ID = frame.frameId(); message.LEN = static_cast(payloadSize); message.MSGTYPE = frame.hasExtendedFrameFormat() ? PCAN_MESSAGE_EXTENDED : PCAN_MESSAGE_STANDARD; if (frame.frameType() == QCanBusFrame::RemoteRequestFrame) message.MSGTYPE |= PCAN_MESSAGE_RTR; // we do not care about the payload else ::memcpy(message.DATA, payload.constData(), payloadSize); st = ::CAN_Write(channelIndex, &message); } 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(); } void PeakCanBackendPrivate::startRead() { Q_Q(PeakCanBackend); QList newFrames; for (;;) { if (isFlexibleDatarateEnabled) { TPCANMsgFD message = {}; TPCANTimestampFD timestamp = {}; const TPCANStatus st = ::CAN_ReadFD(channelIndex, &message, ×tamp); if (st != PCAN_ERROR_OK) { if (Q_UNLIKELY(st != PCAN_ERROR_QRCVEMPTY)) q->setError(systemErrorString(st), QCanBusDevice::ReadError); break; } // Filter out PCAN status frames, to avoid turning them // into QCanBusFrame::DataFrames with random canId if (Q_UNLIKELY(message.MSGTYPE & PCAN_MESSAGE_STATUS)) continue; const int size = dlcToSize(static_cast(message.DLC)); QCanBusFrame frame(message.ID, QByteArray(reinterpret_cast(message.DATA), size)); frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(static_cast(timestamp))); frame.setExtendedFrameFormat(message.MSGTYPE & PCAN_MESSAGE_EXTENDED); frame.setFrameType((message.MSGTYPE & PCAN_MESSAGE_RTR) ? QCanBusFrame::RemoteRequestFrame : QCanBusFrame::DataFrame); frame.setFlexibleDataRateFormat(message.MSGTYPE & PCAN_MESSAGE_FD); frame.setBitrateSwitch(message.MSGTYPE & PCAN_MESSAGE_BRS); frame.setErrorStateIndicator(message.MSGTYPE & PCAN_MESSAGE_ESI); newFrames.append(std::move(frame)); } else { TPCANMsg message = {}; TPCANTimestamp timestamp = {}; const TPCANStatus st = ::CAN_Read(channelIndex, &message, ×tamp); if (st != PCAN_ERROR_OK) { if (Q_UNLIKELY(st != PCAN_ERROR_QRCVEMPTY)) q->setError(systemErrorString(st), QCanBusDevice::ReadError); break; } // Filter out PCAN status frames, to avoid turning them // into QCanBusFrame::DataFrames with random canId if (Q_UNLIKELY(message.MSGTYPE & PCAN_MESSAGE_STATUS)) continue; const int size = static_cast(message.LEN); QCanBusFrame frame(message.ID, QByteArray(reinterpret_cast(message.DATA), size)); const quint64 millis = timestamp.millis + Q_UINT64_C(0x100000000) * timestamp.millis_overflow; const quint64 micros = Q_UINT64_C(1000) * millis + timestamp.micros; frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(static_cast(micros))); frame.setExtendedFrameFormat(message.MSGTYPE & PCAN_MESSAGE_EXTENDED); frame.setFrameType((message.MSGTYPE & PCAN_MESSAGE_RTR) ? QCanBusFrame::RemoteRequestFrame : QCanBusFrame::DataFrame); newFrames.append(std::move(frame)); } } q->enqueueReceivedFrames(newFrames); } bool PeakCanBackendPrivate::verifyBitRate(int bitrate) { Q_Q(PeakCanBackend); if (Q_UNLIKELY(isOpen)) { 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; } bool isValidBitrate = false; if (q->configurationParameter(QCanBusDevice::CanFdKey).toBool()) isValidBitrate = !nominalBitrateString(bitrate).isEmpty(); else 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); } return isValidBitrate; } PeakCanBackend::PeakCanBackend(const QString &name, QObject *parent) : QCanBusDevice(parent) , d_ptr(new PeakCanBackendPrivate(this)) { Q_D(PeakCanBackend); d->setupChannel(name.toLatin1()); d->setupDefaultConfigurations(); } PeakCanBackend::~PeakCanBackend() { Q_D(PeakCanBackend); if (d->isOpen) close(); delete d_ptr; } bool PeakCanBackend::open() { Q_D(PeakCanBackend); if (!d->isOpen) { if (Q_UNLIKELY(!d->open())) return false; // Apply all stored configurations except bitrate, because // the bitrate cannot be changed after opening the device const auto keys = configurationKeys(); for (ConfigurationKey key : keys) { if (key == QCanBusDevice::BitRateKey || key == QCanBusDevice::DataBitRateKey) continue; const QVariant param = configurationParameter(key); const bool success = d->setConfigurationParameter(key, param); if (Q_UNLIKELY(!success)) { qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot apply parameter: %d with value: %ls.", key, qUtf16Printable(param.toString())); } } } setState(QCanBusDevice::ConnectedState); return true; } void PeakCanBackend::close() { Q_D(PeakCanBackend); d->close(); setState(QCanBusDevice::UnconnectedState); } void PeakCanBackend::setConfigurationParameter(ConfigurationKey key, const QVariant &value) { Q_D(PeakCanBackend); if (d->setConfigurationParameter(key, value)) QCanBusDevice::setConfigurationParameter(key, value); } bool PeakCanBackend::writeFrame(const QCanBusFrame &newData) { Q_D(PeakCanBackend); if (Q_UNLIKELY(state() != QCanBusDevice::ConnectedState)) return false; if (Q_UNLIKELY(!newData.isValid())) { setError(tr("Cannot write invalid QCanBusFrame"), QCanBusDevice::WriteError); return false; } if (Q_UNLIKELY(newData.frameType() != QCanBusFrame::DataFrame && newData.frameType() != QCanBusFrame::RemoteRequestFrame)) { setError(tr("Unable to write a frame with unacceptable type"), QCanBusDevice::WriteError); return false; } enqueueOutgoingFrame(newData); if (!d->writeNotifier->isActive()) d->writeNotifier->start(); return true; } // TODO: Implement me QString PeakCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame) { Q_UNUSED(errorFrame); return QString(); } void PeakCanBackend::resetController() { close(); open(); } bool PeakCanBackend::hasBusStatus() const { return true; } QCanBusDevice::CanBusStatus PeakCanBackend::busStatus() { 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