Source code
Revision control
Copy as Markdown
// Copyright (C) 2017 Denis Shienkov <denis.shienkov@gmail.com>
// Copyright (c) 2020 Andre Hartmann <aha_1980@gmx.de>
// 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
// Qt-Security score:critical reason:data-parser
#include "peakcanbackend.h"
#include "peakcanbackend_p.h"
#include "peakcan_symbols_p.h"
#include <QtSerialBus/qcanbusdevice.h>
#include <QtCore/qtimer.h>
#include <QtCore/qcoreevent.h>
#include <QtCore/qloggingcategory.h>
#include <algorithm>
#include <vector>
#ifdef Q_OS_WIN32
# include <QtCore/qwineventnotifier.h>
#else
# include <QtCore/qsocketnotifier.h>
#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<QCanBusDeviceInfo> PeakCanBackend::interfacesByChannelCondition(Availability available)
{
QList<QCanBusDeviceInfo> 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<QCanBusDeviceInfo> 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<TPCANChannelInformation> 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<QCanBusDeviceInfo> 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<QCanBusDeviceInfo> PeakCanBackend::attachedInterfaces(Availability available)
{
#ifdef Q_OS_WIN
bool ok = false;
QList<QCanBusDeviceInfo> attachedChannelsResult = interfacesByAttachedChannels(available, &ok);
if (ok)
return attachedChannelsResult;
#endif
QList<QCanBusDeviceInfo> result = interfacesByChannelCondition(available);
return result;
}
QList<QCanBusDeviceInfo> PeakCanBackend::interfaces()
{
return attachedInterfaces(Availability::Available);
}
QCanBusDeviceInfo PeakCanBackend::deviceInfo() const
{
const uint index = d_ptr->channelIndex;
const QString name = pcanChannelNameForIndex(index);
const QList<QCanBusDeviceInfo> 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<char *>(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 channel %u hardware: %ls",
uint(channelIndex), 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 channel %u receive event handler: %ls",
uint(channelIndex), 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)) {
const QString errorString = systemErrorString(err);
qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot register channel %u receive event handler: %ls",
uint(channelIndex), qUtf16Printable(errorString));
q->setError(errorString, 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 channel %u receive event handler: %ls",
uint(channelIndex), 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)) {
const QString errorString = systemErrorString(st);
qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot uninitialize channel %u: %ls",
uint(channelIndex), qUtf16Printable(errorString));
q->setError(errorString, 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.constData(), -1);
}
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<CanFrameDlc>(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<int>(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<quint8>(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<QCanBusFrame> 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)) {
const QString errorString = systemErrorString(st);
qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot read frame: %ls",
qUtf16Printable(errorString));
q->setError(errorString, 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;
if (Q_UNLIKELY(message.DLC > 15))
continue;
qsizetype size = std::min(qsizetype(message.DLC), qsizetype(8));
if (message.MSGTYPE & PCAN_MESSAGE_FD)
size = dlcToSize(static_cast<CanFrameDlc>(message.DLC));
QCanBusFrame frame(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);
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)) {
const QString errorString = systemErrorString(st);
qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot read frame: %ls",
qUtf16Printable(errorString));
q->setError(errorString, 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 qsizetype size = std::min(qsizetype(message.LEN),
qsizetype(sizeof(message.DATA)));
QCanBusFrame frame(message.ID, QByteArray(reinterpret_cast<const char *>(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<qint64>(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())) {
const QString errorString = tr("Cannot write invalid QCanBusFrame");
qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "%ls", qUtf16Printable(errorString));
setError(errorString, QCanBusDevice::WriteError);
return false;
}
if (Q_UNLIKELY(newData.frameType() != QCanBusFrame::DataFrame
&& newData.frameType() != QCanBusFrame::RemoteRequestFrame)) {
const QString errorString = tr("Cannot write QCanBusFrame with type %1").arg(newData.frameType());
qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "%ls", qUtf16Printable(errorString));
setError(errorString, 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