Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d65c829d authored by Chris Weir's avatar Chris Weir Committed by Android (Google) Code Review
Browse files

Merge "Add support for error, RTR, and EFF frames"

parents cbe5722a bee3d2c3
Loading
Loading
Loading
Loading
+70 −13
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@
#include <libnetdevice/can.h>
#include <libnetdevice/libnetdevice.h>
#include <linux/can.h>
#include <linux/can/error.h>
#include <linux/can/raw.h>

namespace android {
namespace hardware {
@@ -219,6 +221,21 @@ bool CanBus::down() {
    return success;
}

/**
 * Helper function to determine if a flag meets the requirements of a
 * FilterFlag. See definition of FilterFlag in types.hal
 *
 * \param filterFlag FilterFlag object to match flag against
 * \param flag bool object from CanMessage object
 */
static bool satisfiesFilterFlag(FilterFlag filterFlag, bool flag) {
    // TODO(b/144458917) add testing for this to VTS tests
    if (filterFlag == FilterFlag::DONT_CARE) return true;
    if (filterFlag == FilterFlag::REQUIRE) return flag;
    if (filterFlag == FilterFlag::EXCLUDE) return !flag;
    return false;
}

/**
 * Match the filter set against message id.
 *
@@ -229,13 +246,16 @@ bool CanBus::down() {
 * \param id Message id to filter
 * \return true if the message id matches the filter, false otherwise
 */
static bool match(const hidl_vec<CanMessageFilter>& filter, CanMessageId id) {
static bool match(const hidl_vec<CanMessageFilter>& filter, CanMessageId id, bool isExtendedId,
                  bool isRtr) {
    if (filter.size() == 0) return true;

    bool anyNonInvertedPresent = false;
    bool anyNonInvertedSatisfied = false;
    for (auto& rule : filter) {
        const bool satisfied = ((id & rule.mask) == rule.id) == !rule.inverted;
        const bool satisfied = ((id & rule.mask) == rule.id) == !rule.inverted &&
                               satisfiesFilterFlag(rule.rtr, isRtr) &&
                               satisfiesFilterFlag(rule.extendedFormat, isExtendedId);
        if (rule.inverted) {
            // Any inverted (blacklist) rule not being satisfied invalidates the whole filter set.
            if (!satisfied) return false;
@@ -247,11 +267,54 @@ static bool match(const hidl_vec<CanMessageFilter>& filter, CanMessageId id) {
    return !anyNonInvertedPresent || anyNonInvertedSatisfied;
}

void CanBus::notifyErrorListeners(ErrorEvent err, bool isFatal) {
    std::lock_guard<std::mutex> lck(mErrListenersGuard);
    for (auto& listener : mErrListeners) {
        if (!listener->onError(err, isFatal).isOk()) {
            LOG(WARNING) << "Failed to notify listener about error";
        }
    }
}

static ErrorEvent parseErrorFrame(const struct canfd_frame& frame) {
    // decode error frame (to a degree)
    if ((frame.can_id & (CAN_ERR_BUSERROR | CAN_ERR_BUSOFF)) != 0) {
        return ErrorEvent::BUS_ERROR;
    }
    if ((frame.data[1] & CAN_ERR_CRTL_TX_OVERFLOW) != 0) {
        return ErrorEvent::TX_OVERFLOW;
    }
    if ((frame.data[1] & CAN_ERR_CRTL_RX_OVERFLOW) != 0) {
        return ErrorEvent::RX_OVERFLOW;
    }
    if ((frame.data[2] & CAN_ERR_PROT_OVERLOAD) != 0) {
        return ErrorEvent::BUS_OVERLOAD;
    }
    if ((frame.can_id & CAN_ERR_PROT) != 0) {
        return ErrorEvent::MALFORMED_INPUT;
    }
    if ((frame.can_id & (CAN_ERR_CRTL | CAN_ERR_TRX | CAN_ERR_RESTARTED)) != 0) {
        // "controller restarted" constitutes a HARDWARE_ERROR imo
        return ErrorEvent::HARDWARE_ERROR;
    }
    return ErrorEvent::UNKNOWN_ERROR;
}

void CanBus::onRead(const struct canfd_frame& frame, std::chrono::nanoseconds timestamp) {
    if ((frame.can_id & CAN_ERR_FLAG) != 0) {
        // error bit is set
        LOG(WARNING) << "CAN Error frame received";
        // TODO(b/144458917) consider providing different values for isFatal, depending on error
        notifyErrorListeners(parseErrorFrame(frame), false);
        return;
    }

    CanMessage message = {};
    message.id = frame.can_id;
    message.id = frame.can_id & CAN_EFF_MASK;  // mask out eff/rtr/err flags
    message.payload = hidl_vec<uint8_t>(frame.data, frame.data + frame.len);
    message.timestamp = timestamp.count();
    message.isExtendedId = (frame.can_id & CAN_EFF_FLAG) != 0;
    message.remoteTransmissionRequest = (frame.can_id & CAN_RTR_FLAG) != 0;

    if (UNLIKELY(kSuperVerbose)) {
        LOG(VERBOSE) << "Got message " << toString(message);
@@ -259,7 +322,9 @@ void CanBus::onRead(const struct canfd_frame& frame, std::chrono::nanoseconds ti

    std::lock_guard<std::mutex> lck(mMsgListenersGuard);
    for (auto& listener : mMsgListeners) {
        if (!match(listener.filter, message.id)) continue;
        if (!match(listener.filter, message.id, message.remoteTransmissionRequest,
                   message.isExtendedId))
            continue;
        if (!listener.callback->onReceive(message).isOk() && !listener.failedOnce) {
            listener.failedOnce = true;
            LOG(WARNING) << "Failed to notify listener about message";
@@ -274,15 +339,7 @@ void CanBus::onError(int errnoVal) {
        mDownAfterUse = false;
        eventType = ErrorEvent::INTERFACE_DOWN;
    }

    {
        std::lock_guard<std::mutex> lck(mErrListenersGuard);
        for (auto& listener : mErrListeners) {
            if (!listener->onError(eventType, true).isOk()) {
                LOG(WARNING) << "Failed to notify listener about error";
            }
        }
    }
    notifyErrorListeners(eventType, true);

    const auto errcb = mErrCb;
    if (errcb != nullptr) errcb();
+2 −0
Original line number Diff line number Diff line
@@ -89,6 +89,8 @@ struct CanBus : public ICanBus {
    void clearMsgListeners();
    void clearErrListeners();

    void notifyErrorListeners(ErrorEvent err, bool isFatal);

    void onRead(const struct canfd_frame& frame, std::chrono::nanoseconds timestamp);
    void onError(int errnoVal);

+9 −0
Original line number Diff line number Diff line
@@ -24,12 +24,16 @@
#include <android-base/unique_fd.h>

#include <linux/can.h>
#include <linux/can/error.h>
#include <linux/can/netlink.h>
#include <linux/can/raw.h>

namespace android {
namespace netdevice {
namespace can {

static constexpr can_err_mask_t kErrMask = CAN_ERR_MASK;

base::unique_fd socket(const std::string& ifname) {
    struct sockaddr_can addr = {};
    addr.can_family = AF_CAN;
@@ -45,6 +49,11 @@ base::unique_fd socket(const std::string& ifname) {
        return {};
    }

    if (setsockopt(sock.get(), SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &kErrMask, sizeof(kErrMask)) < 0) {
        LOG(ERROR) << "Can't receive error frames, CAN setsockpt failed: " << strerror(errno);
        return {};
    }

    if (0 != fcntl(sock.get(), F_SETFL, O_RDWR | O_NONBLOCK)) {
        LOG(ERROR) << "Couldn't put CAN socket in non-blocking mode";
        return {};
+6 −3
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <android/hidl/manager/1.2/IServiceManager.h>
#include <hidl-utils/hidl-utils.h>

#include <linux/can.h>
#include <chrono>
#include <iomanip>
#include <iostream>
@@ -42,12 +43,14 @@ struct CanMessageListener : public V1_0::ICanMessageListener {
    CanMessageListener(std::string name) : name(name) {}

    virtual Return<void> onReceive(const V1_0::CanMessage& message) {
        std::cout << "  " << name << "  " << std::hex << std::uppercase << std::setw(3)
        int msgIdWidth = 3;
        if (message.isExtendedId) msgIdWidth = 8;
        std::cout << "  " << name << "  " << std::hex << std::uppercase << std::setw(msgIdWidth)
                  << std::setfill('0') << message.id << std::setw(0);
        std::cout << "   [" << message.payload.size() << "] ";
        if (message.remoteTransmissionRequest) {
            std::cout << " RTR";
            std::cout << "remote request";
        } else {
            std::cout << "   [" << message.payload.size() << "] ";
            for (const auto byte : message.payload) {
                std::cout << " " << std::setfill('0') << std::setw(2) << unsigned(byte);
            }
+28 −0
Original line number Diff line number Diff line
@@ -58,6 +58,15 @@ struct CanMessage {
     * If this flag is set, payload must be empty.
     */
    bool remoteTransmissionRequest;

    /**
     * Flag indicating if the message has an extended ID.
     *
     * Extended ID's are 29 bits long, as opposed to the standard 11 bit ID.
     * It can not simply be inferred from the length of the ID itself, as the
     * message ID 0x00000123 !=  message ID 0x123.
     */
    bool isExtendedId;
};

/**
@@ -70,11 +79,30 @@ struct CanMessage {
 * one) and all inverted filters must match. In other words:
 *  - a single matching non-inverted filter makes the whole set matching;
 *  - a single non-matching inverted filter makes the whole set non-matching.
 *
 * Additional less common options for filtering include:
 * rtr - Remote Transmission Request; another ECU requests DLC bytes of data on this message ID
 * extendedFormat - 29 bit message ID is used instead of 11 bits
 */
struct CanMessageFilter {
    CanMessageId id;
    uint32_t mask;
    bool inverted;
    FilterFlag rtr;
    FilterFlag extendedFormat;
};


/**
 * Types of filter that can be applied to a CanMessageFilter
 */
enum FilterFlag : uint8_t {
    /** Default, FilterFlag doesn't effect what messages filtered */
    DONT_CARE = 0,
    /** This FilterFlag MUST be present in received messages to pass though the filter */
    REQUIRE,
    /** This FilterFlag must NOT be present in received messages to pass though the filter */
    EXCLUDE,
};

enum Result : uint8_t {
Loading