Revision control
Copy as Markdown
// 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 "socketcanbackend.h"
#include "libsocketcan.h"
#include <QtSerialBus/qcanbusdevice.h>
#include <QtCore/qdatastream.h>
#include <QtCore/qdebug.h>
#include <QtCore/qdiriterator.h>
#include <QtCore/qfile.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qsocketnotifier.h>
#include <linux/can/error.h>
#include <linux/can/raw.h>
#include <linux/sockios.h>
#include <errno.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#ifndef CANFD_BRS
# define CANFD_BRS 0x01 /* bit rate switch (second bitrate for payload data) */
#endif
#ifndef CANFD_ESI
# define CANFD_ESI 0x02 /* error state indicator of the transmitting node */
#endif
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_SOCKETCAN)
const char sysClassNetC[] = "/sys/class/net/";
const char interfaceC[] = "/device/interface";
const char devIdC[] = "/dev_id";
const char flagsC[] = "/flags";
const char mtuC[] = "/mtu";
const char typeC[] = "/type";
const char virtualC[] = "virtual";
enum {
CanFlexibleDataRateMtu = 72,
TypeSocketCan = 280,
DeviceIsActive = 1
};
static QByteArray fileContent(const QString &fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly))
return QByteArray();
return file.readAll().trimmed();
}
static bool isFlexibleDataRateCapable(const QString &canDevice)
{
const QString path = QLatin1String(sysClassNetC) + canDevice + QLatin1String(mtuC);
const int mtu = fileContent(path).toInt();
return mtu == CanFlexibleDataRateMtu;
}
static bool isVirtual(const QString &canDevice)
{
const QFileInfo fi(QLatin1String(sysClassNetC) + canDevice);
return fi.canonicalPath().contains(QLatin1String(virtualC));
}
static quint32 flags(const QString &canDevice)
{
const QString path = QLatin1String(sysClassNetC) + canDevice + QLatin1String(flagsC);
const quint32 result = fileContent(path).toUInt(nullptr, 0);
return result;
}
static QString deviceDescription(const QString &canDevice)
{
const QString path = QLatin1String(sysClassNetC) + canDevice + QLatin1String(interfaceC);
const QByteArray content = fileContent(path);
if (content.isEmpty() && isVirtual(canDevice))
return QStringLiteral("Virtual CAN");
return QString::fromUtf8(content);
}
static int deviceChannel(const QString &canDevice)
{
const QString path = QLatin1String(sysClassNetC) + canDevice + QLatin1String(devIdC);
const QByteArray content = fileContent(path);
return content.toInt(nullptr, 0);
}
QCanBusDeviceInfo SocketCanBackend::socketCanDeviceInfo(const QString &deviceName)
{
const QString serial; // exists for code readability purposes only
const QString alias; // exists for code readability purposes only
const QString description = deviceDescription(deviceName);
const int channel = deviceChannel(deviceName);
return createDeviceInfo(QStringLiteral("socketcan"), deviceName,
serial, description,
alias, channel, isVirtual(deviceName),
isFlexibleDataRateCapable(deviceName));
}
QList<QCanBusDeviceInfo> SocketCanBackend::interfaces()
{
QList<QCanBusDeviceInfo> result;
QDirIterator it(sysClassNetC,
QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot,
QDirIterator::Subdirectories);
while (it.hasNext()) {
const QString dirEntry = it.next();
if (fileContent(dirEntry + QLatin1String(typeC)).toInt() != TypeSocketCan)
continue;
const QString deviceName = dirEntry.mid(strlen(sysClassNetC));
if (!(flags(deviceName) & DeviceIsActive))
continue;
result.append(socketCanDeviceInfo(deviceName));
}
std::sort(result.begin(), result.end(),
[](const QCanBusDeviceInfo &a, const QCanBusDeviceInfo &b) {
return a.name() < b.name();
});
return result;
}
QCanBusDeviceInfo SocketCanBackend::deviceInfo() const
{
return socketCanDeviceInfo(canSocketName);
}
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();
}
SocketCanBackend::~SocketCanBackend()
{
close();
}
void SocketCanBackend::resetConfigurations()
{
QCanBusDevice::setConfigurationParameter(
QCanBusDevice::LoopbackKey, true);
QCanBusDevice::setConfigurationParameter(
QCanBusDevice::ReceiveOwnKey, false);
QCanBusDevice::setConfigurationParameter(
QCanBusDevice::ErrorFilterKey,
QVariant::fromValue(QCanBusFrame::FrameErrors(QCanBusFrame::AnyError)));
QCanBusDevice::setConfigurationParameter(
QCanBusDevice::CanFdKey, false);
QCanBusDevice::setConfigurationParameter(
QCanBusDevice::BitRateKey, QVariant());
}
bool SocketCanBackend::open()
{
if (canSocket == -1) {
if (!connectSocket()) {
close(); // sets UnconnectedState
return false;
}
}
setState(QCanBusDevice::ConnectedState);
return true;
}
void SocketCanBackend::close()
{
::close(canSocket);
canSocket = -1;
setState(QCanBusDevice::UnconnectedState);
}
bool SocketCanBackend::applyConfigurationParameter(ConfigurationKey key, const QVariant &value)
{
bool success = false;
switch (key) {
case QCanBusDevice::LoopbackKey:
{
const int loopback = value.toBool() ? 1 : 0;
if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_LOOPBACK,
&loopback, sizeof(loopback)) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConfigurationError);
break;
}
success = true;
break;
}
case QCanBusDevice::ReceiveOwnKey:
{
const int receiveOwnMessages = value.toBool() ? 1 : 0;
if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS,
&receiveOwnMessages, sizeof(receiveOwnMessages)) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConfigurationError);
break;
}
success = true;
break;
}
case QCanBusDevice::ErrorFilterKey:
{
const int errorMask = value.value<QCanBusFrame::FrameErrors>();
if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
&errorMask, sizeof(errorMask)) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConfigurationError);
break;
}
success = true;
break;
}
case QCanBusDevice::RawFilterKey:
{
const QList<QCanBusDevice::Filter> filterList
= value.value<QList<QCanBusDevice::Filter> >();
if (!value.isValid() || filterList.isEmpty()) {
// permit every frame - no restrictions (filter reset)
can_filter filters = {0, 0};
socklen_t s = sizeof(can_filter);
if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_FILTER,
&filters, s) != 0)) {
qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Cannot unset socket filters.");
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConfigurationError);
break;
}
success = true;
break;
}
QList<can_filter> filters;
filters.resize(filterList.size());
for (int i = 0; i < filterList.size(); i++) {
const QCanBusDevice::Filter f = filterList.at(i);
can_filter filter = { f.frameId, f.frameIdMask };
// frame type filter
switch (f.type) {
default:
// any other type cannot be filtered upon
setError(tr("Cannot set filter for frame type: %1").arg(f.type),
QCanBusDevice::CanBusError::ConfigurationError);
return false;
case QCanBusFrame::InvalidFrame:
break;
case QCanBusFrame::DataFrame:
filter.can_mask |= CAN_RTR_FLAG;
break;
case QCanBusFrame::ErrorFrame:
filter.can_mask |= CAN_ERR_FLAG;
filter.can_id |= CAN_ERR_FLAG;
break;
case QCanBusFrame::RemoteRequestFrame:
filter.can_mask |= CAN_RTR_FLAG;
filter.can_id |= CAN_RTR_FLAG;
break;
}
// frame format filter
if ((f.format & QCanBusDevice::Filter::MatchBaseAndExtendedFormat)
== QCanBusDevice::Filter::MatchBaseAndExtendedFormat) {
// nothing
} else if (f.format & QCanBusDevice::Filter::MatchBaseFormat) {
filter.can_mask |= CAN_EFF_FLAG;
} else if (f.format & QCanBusDevice::Filter::MatchExtendedFormat) {
filter.can_mask |= CAN_EFF_FLAG;
filter.can_id |= CAN_EFF_FLAG;
}
filters[i] = filter;
}
if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_FILTER,
filters.constData(), sizeof(filters[0]) * filters.size()) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConfigurationError);
break;
}
success = true;
break;
}
case QCanBusDevice::CanFdKey:
{
const int fd_frames = value.toBool() ? 1 : 0;
if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_FD_FRAMES,
&fd_frames, sizeof(fd_frames)) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConfigurationError);
break;
}
success = true;
break;
}
case QCanBusDevice::BitRateKey:
{
if (!value.isNull()) {
const quint32 bitRate = value.toUInt();
success = libSocketCan->setBitrate(canSocketName, bitRate);
}
break;
}
default:
setError(tr("Unsupported configuration key: %1").arg(key),
QCanBusDevice::CanBusError::ConfigurationError);
break;
}
return success;
}
bool SocketCanBackend::connectSocket()
{
struct ifreq interface;
if (Q_UNLIKELY((canSocket = socket(PF_CAN, SOCK_RAW | SOCK_NONBLOCK, protocol)) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConnectionError);
return false;
}
qstrncpy(interface.ifr_name, canSocketName.toLatin1().constData(), sizeof(interface.ifr_name));
if (Q_UNLIKELY(ioctl(canSocket, SIOCGIFINDEX, &interface) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConnectionError);
return false;
}
m_address.can_family = AF_CAN;
m_address.can_ifindex = interface.ifr_ifindex;
if (Q_UNLIKELY(bind(canSocket, reinterpret_cast<struct sockaddr *>(&m_address), sizeof(m_address)) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConnectionError);
return false;
}
m_iov.iov_base = &m_frame;
m_msg.msg_name = &m_address;
m_msg.msg_iov = &m_iov;
m_msg.msg_iovlen = 1;
m_msg.msg_control = &m_ctrlmsg;
delete notifier;
notifier = new QSocketNotifier(canSocket, QSocketNotifier::Read, this);
connect(notifier, &QSocketNotifier::activated,
this, &SocketCanBackend::readSocket);
//apply all stored configurations
const auto keys = configurationKeys();
for (ConfigurationKey key : keys) {
const QVariant param = configurationParameter(key);
bool success = applyConfigurationParameter(key, param);
if (Q_UNLIKELY(!success)) {
qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Cannot apply parameter: %d with value: %ls.",
key, qUtf16Printable(param.toString()));
}
}
return true;
}
void SocketCanBackend::setConfigurationParameter(ConfigurationKey key, const QVariant &value)
{
if (key == QCanBusDevice::RawFilterKey) {
//verify valid/supported filters
const auto filters = value.value<QList<QCanBusDevice::Filter> >();
for (QCanBusDevice::Filter f : filters) {
switch (f.type) {
case QCanBusFrame::UnknownFrame:
default:
setError(tr("Cannot set filter for frame type: %1").arg(f.type),
QCanBusDevice::CanBusError::ConfigurationError);
return;
case QCanBusFrame::InvalidFrame:
case QCanBusFrame::DataFrame:
case QCanBusFrame::ErrorFrame:
case QCanBusFrame::RemoteRequestFrame:
break;
}
if (f.frameId > 0x1FFFFFFFU) {
setError(tr("FrameId %1 larger than 29 bit.").arg(f.frameId),
QCanBusDevice::CanBusError::ConfigurationError);
return;
}
}
} else if (key == QCanBusDevice::ProtocolKey) {
bool ok = false;
const int newProtocol = value.toInt(&ok);
if (Q_UNLIKELY(!ok || (newProtocol < 0))) {
const QString errorString = tr("Cannot set protocol to value %1.").arg(value.toString());
setError(errorString, QCanBusDevice::ConfigurationError);
qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls", qUtf16Printable(errorString));
return;
}
protocol = newProtocol;
}
// connected & params not applyable/invalid
if (canSocket != -1 && !applyConfigurationParameter(key, value))
return;
QCanBusDevice::setConfigurationParameter(key, value);
// we need to check CAN FD option a lot -> cache it and avoid QList lookup
if (key == QCanBusDevice::CanFdKey)
canFdOptionEnabled = value.toBool();
}
bool SocketCanBackend::writeFrame(const QCanBusFrame &newData)
{
if (state() != ConnectedState)
return false;
if (Q_UNLIKELY(!newData.isValid())) {
setError(tr("Cannot write invalid QCanBusFrame"), QCanBusDevice::WriteError);
return false;
}
canid_t canId = newData.frameId();
if (newData.hasExtendedFrameFormat())
canId |= CAN_EFF_FLAG;
if (newData.frameType() == QCanBusFrame::RemoteRequestFrame) {
canId |= CAN_RTR_FLAG;
} else if (newData.frameType() == QCanBusFrame::ErrorFrame) {
canId = static_cast<canid_t>((newData.error() & QCanBusFrame::AnyError));
canId |= CAN_ERR_FLAG;
}
if (Q_UNLIKELY(!canFdOptionEnabled && newData.hasFlexibleDataRateFormat())) {
const QString error = tr("Cannot write CAN FD frame because CAN FD option is not enabled.");
qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls", qUtf16Printable(error));
setError(error, QCanBusDevice::WriteError);
return false;
}
qint64 bytesWritten = 0;
if (newData.hasFlexibleDataRateFormat()) {
canfd_frame frame = {};
frame.len = newData.payload().size();
frame.can_id = canId;
frame.flags = newData.hasBitrateSwitch() ? CANFD_BRS : 0;
frame.flags |= newData.hasErrorStateIndicator() ? CANFD_ESI : 0;
::memcpy(frame.data, newData.payload().constData(), frame.len);
bytesWritten = ::write(canSocket, &frame, sizeof(frame));
} else {
can_frame frame = {};
frame.can_dlc = newData.payload().size();
frame.can_id = canId;
::memcpy(frame.data, newData.payload().constData(), frame.can_dlc);
bytesWritten = ::write(canSocket, &frame, sizeof(frame));
}
if (Q_UNLIKELY(bytesWritten < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::WriteError);
return false;
}
emit framesWritten(1);
return true;
}
QString SocketCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame)
{
if (errorFrame.frameType() != QCanBusFrame::ErrorFrame)
return QString();
// the payload may contain the error details
const QByteArray data = errorFrame.payload();
QString errorMsg;
if (errorFrame.error() & QCanBusFrame::TransmissionTimeoutError)
errorMsg += QStringLiteral("TX timeout\n");
if (errorFrame.error() & QCanBusFrame::MissingAcknowledgmentError)
errorMsg += QStringLiteral("Received no ACK on transmission\n");
if (errorFrame.error() & QCanBusFrame::BusOffError)
errorMsg += QStringLiteral("Bus off\n");
if (errorFrame.error() & QCanBusFrame::BusError)
errorMsg += QStringLiteral("Bus error\n");
if (errorFrame.error() & QCanBusFrame::ControllerRestartError)
errorMsg += QStringLiteral("Controller restarted\n");
if (errorFrame.error() & QCanBusFrame::UnknownError)
errorMsg += QStringLiteral("Unknown error\n");
if (errorFrame.error() & QCanBusFrame::LostArbitrationError) {
errorMsg += QStringLiteral("Lost arbitration:\n");
if (data.size() >= 1) {
errorMsg += QString::number(data.at(0), 16);
errorMsg += QStringLiteral(" bit\n");
}
}
if (errorFrame.error() & QCanBusFrame::ControllerError) {
errorMsg += QStringLiteral("Controller problem:\n");
if (data.size() >= 2) {
char b = data.at(1) ;
if (b & CAN_ERR_CRTL_RX_OVERFLOW)
errorMsg += QStringLiteral(" RX buffer overflow\n");
if (b & CAN_ERR_CRTL_TX_OVERFLOW)
errorMsg += QStringLiteral(" TX buffer overflow\n");
if (b & CAN_ERR_CRTL_RX_WARNING)
errorMsg += QStringLiteral(" reached warning level for RX errors\n");
if (b & CAN_ERR_CRTL_TX_WARNING)
errorMsg += QStringLiteral(" reached warning level for TX errors\n");
if (b & CAN_ERR_CRTL_RX_PASSIVE)
errorMsg += QStringLiteral(" reached error passive status RX\n");
if (b & CAN_ERR_CRTL_TX_PASSIVE)
errorMsg += QStringLiteral(" reached error passive status TX\n");
if (b == CAN_ERR_CRTL_UNSPEC)
errorMsg += QStringLiteral(" Unspecified error\n");
}
}
if (errorFrame.error() & QCanBusFrame::TransceiverError) {
errorMsg = QStringLiteral("Transceiver status:");
if (data.size() >= 5) {
char b = data.at(4);
if (b & CAN_ERR_TRX_CANH_NO_WIRE)
errorMsg += QStringLiteral(" CAN-transceiver CANH no wire\n");
if (b & CAN_ERR_TRX_CANH_SHORT_TO_BAT)
errorMsg += QStringLiteral(" CAN-transceiver CANH short to bat\n");
if (b & CAN_ERR_TRX_CANH_SHORT_TO_VCC)
errorMsg += QStringLiteral(" CAN-transceiver CANH short to vcc\n");
if (b & CAN_ERR_TRX_CANH_SHORT_TO_GND)
errorMsg += QStringLiteral(" CAN-transceiver CANH short to ground\n");
if (b & CAN_ERR_TRX_CANL_NO_WIRE)
errorMsg += QStringLiteral(" CAN-transceiver CANL no wire\n");
if (b & CAN_ERR_TRX_CANL_SHORT_TO_BAT)
errorMsg += QStringLiteral(" CAN-transceiver CANL short to bat\n");
if (b & CAN_ERR_TRX_CANL_SHORT_TO_VCC)
errorMsg += QStringLiteral(" CAN-transceiver CANL short to vcc\n");
if (b & CAN_ERR_TRX_CANL_SHORT_TO_GND)
errorMsg += QStringLiteral(" CAN-transceiver CANL short to ground\n");
if (b & CAN_ERR_TRX_CANL_SHORT_TO_CANH)
errorMsg += QStringLiteral(" CAN-transceiver CANL short to CANH\n");
if (b == CAN_ERR_TRX_UNSPEC)
errorMsg += QStringLiteral(" unspecified\n");
}
}
if (errorFrame.error() & QCanBusFrame::ProtocolViolationError) {
errorMsg += QStringLiteral("Protocol violation:\n");
if (data.size() > 3) {
char b = data.at(2);
if (b & CAN_ERR_PROT_BIT)
errorMsg += QStringLiteral(" single bit error\n");
if (b & CAN_ERR_PROT_FORM)
errorMsg += QStringLiteral(" frame format error\n");
if (b & CAN_ERR_PROT_STUFF)
errorMsg += QStringLiteral(" bit stuffing error\n");
if (b & CAN_ERR_PROT_BIT0)
errorMsg += QStringLiteral(" unable to send dominant bit\n");
if (b & CAN_ERR_PROT_BIT1)
errorMsg += QStringLiteral(" unable to send recessive bit\n");
if (b & CAN_ERR_PROT_OVERLOAD)
errorMsg += QStringLiteral(" bus overload\n");
if (b & CAN_ERR_PROT_ACTIVE)
errorMsg += QStringLiteral(" active error announcement\n");
if (b & CAN_ERR_PROT_TX)
errorMsg += QStringLiteral(" error occurred on transmission\n");
if (b == CAN_ERR_PROT_UNSPEC)
errorMsg += QStringLiteral(" unspecified\n");
}
if (data.size() > 4) {
char b = data.at(3);
if (b == CAN_ERR_PROT_LOC_SOF)
errorMsg += QStringLiteral(" start of frame\n");
if (b == CAN_ERR_PROT_LOC_ID28_21)
errorMsg += QStringLiteral(" ID bits 28 - 21 (SFF: 10 - 3)\n");
if (b == CAN_ERR_PROT_LOC_ID20_18)
errorMsg += QStringLiteral(" ID bits 20 - 18 (SFF: 2 - 0 )\n");
if (b == CAN_ERR_PROT_LOC_SRTR)
errorMsg += QStringLiteral(" substitute RTR (SFF: RTR)\n");
if (b == CAN_ERR_PROT_LOC_IDE)
errorMsg += QStringLiteral(" identifier extension\n");
if (b == CAN_ERR_PROT_LOC_ID17_13)
errorMsg += QStringLiteral(" ID bits 17-13\n");
if (b == CAN_ERR_PROT_LOC_ID12_05)
errorMsg += QStringLiteral(" ID bits 12-5\n");
if (b == CAN_ERR_PROT_LOC_ID04_00)
errorMsg += QStringLiteral(" ID bits 4-0\n");
if (b == CAN_ERR_PROT_LOC_RTR)
errorMsg += QStringLiteral(" RTR\n");
if (b == CAN_ERR_PROT_LOC_RES1)
errorMsg += QStringLiteral(" reserved bit 1\n");
if (b == CAN_ERR_PROT_LOC_RES0)
errorMsg += QStringLiteral(" reserved bit 0\n");
if (b == CAN_ERR_PROT_LOC_DLC)
errorMsg += QStringLiteral(" data length code\n");
if (b == CAN_ERR_PROT_LOC_DATA)
errorMsg += QStringLiteral(" data section\n");
if (b == CAN_ERR_PROT_LOC_CRC_SEQ)
errorMsg += QStringLiteral(" CRC sequence\n");
if (b == CAN_ERR_PROT_LOC_CRC_DEL)
errorMsg += QStringLiteral(" CRC delimiter\n");
if (b == CAN_ERR_PROT_LOC_ACK)
errorMsg += QStringLiteral(" ACK slot\n");
if (b == CAN_ERR_PROT_LOC_ACK_DEL)
errorMsg += QStringLiteral(" ACK delimiter\n");
if (b == CAN_ERR_PROT_LOC_EOF)
errorMsg += QStringLiteral(" end of frame\n");
if (b == CAN_ERR_PROT_LOC_INTERM)
errorMsg += QStringLiteral(" Intermission\n");
if (b == CAN_ERR_PROT_LOC_UNSPEC)
errorMsg += QStringLiteral(" unspecified\n");
}
}
// cut trailing \n
if (!errorMsg.isEmpty())
errorMsg.chop(1);
return errorMsg;
}
void SocketCanBackend::readSocket()
{
QList<QCanBusFrame> newFrames;
for (;;) {
m_frame = {};
m_iov.iov_len = sizeof(m_frame);
m_msg.msg_namelen = sizeof(m_addr);
m_msg.msg_controllen = sizeof(m_ctrlmsg);
m_msg.msg_flags = 0;
const int bytesReceived = ::recvmsg(canSocket, &m_msg, 0);
if (bytesReceived <= 0) {
break;
} else if (Q_UNLIKELY(bytesReceived != CANFD_MTU && bytesReceived != CAN_MTU)) {
setError(tr("ERROR SocketCanBackend: incomplete CAN frame"),
QCanBusDevice::CanBusError::ReadError);
continue;
} else if (Q_UNLIKELY(m_frame.len > bytesReceived - offsetof(canfd_frame, data))) {
setError(tr("ERROR SocketCanBackend: invalid CAN frame length"),
QCanBusDevice::CanBusError::ReadError);
continue;
}
struct timeval timeStamp = {};
if (Q_UNLIKELY(ioctl(canSocket, SIOCGSTAMP, &timeStamp) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ReadError);
timeStamp = {};
}
const QCanBusFrame::TimeStamp stamp(timeStamp.tv_sec, timeStamp.tv_usec);
QCanBusFrame bufferedFrame;
bufferedFrame.setTimeStamp(stamp);
bufferedFrame.setFlexibleDataRateFormat(bytesReceived == CANFD_MTU);
bufferedFrame.setExtendedFrameFormat(m_frame.can_id & CAN_EFF_FLAG);
Q_ASSERT(m_frame.len <= CANFD_MAX_DLEN);
if (m_frame.can_id & CAN_RTR_FLAG)
bufferedFrame.setFrameType(QCanBusFrame::RemoteRequestFrame);
if (m_frame.can_id & CAN_ERR_FLAG)
bufferedFrame.setFrameType(QCanBusFrame::ErrorFrame);
if (m_frame.flags & CANFD_BRS)
bufferedFrame.setBitrateSwitch(true);
if (m_frame.flags & CANFD_ESI)
bufferedFrame.setErrorStateIndicator(true);
if (m_msg.msg_flags & MSG_CONFIRM)
bufferedFrame.setLocalEcho(true);
bufferedFrame.setFrameId(m_frame.can_id & CAN_EFF_MASK);
const QByteArray load(reinterpret_cast<char *>(m_frame.data), m_frame.len);
bufferedFrame.setPayload(load);
newFrames.append(std::move(bufferedFrame));
}
enqueueReceivedFrames(newFrames);
}
void SocketCanBackend::resetController()
{
libSocketCan->restart(canSocketName);
}
bool SocketCanBackend::hasBusStatus() const
{
if (isVirtual(canSocketName.toLatin1()))
return false;
return libSocketCan->hasBusStatus();
}
QCanBusDevice::CanBusStatus SocketCanBackend::busStatus()
{
return libSocketCan->busStatus(canSocketName);
}
QT_END_NAMESPACE