Add support for error, RTR, and EFF frames

Error frames, remote transmission request, and extended format frames
require some changes to the way that we set up our sockets.

Bug: 142655821
Bug: 144774939
Test: manual
Change-Id: I06212cb852d480c1c7093e8c509ca8aa9f85f81f
This commit is contained in:
chrisweir 2019-11-19 10:23:37 -08:00
parent f72f955b4a
commit bee3d2c3c9
7 changed files with 122 additions and 23 deletions

View file

@ -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();

View file

@ -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);

View file

@ -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 {};

View file

@ -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);
}

View file

@ -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 {

View file

@ -123,9 +123,9 @@ TEST_F(CanBusHalTest, ListenNoFilter) {
TEST_F(CanBusHalTest, ListenSomeFilter) {
hidl_vec<CanMessageFilter> filters = {
{0x123, 0x1FF, false},
{0x001, 0x00F, true},
{0x200, 0x100, false},
{0x123, 0x1FF, false, FilterFlag::DONT_CARE, FilterFlag::DONT_CARE},
{0x001, 0x00F, true, FilterFlag::DONT_CARE, FilterFlag::DONT_CARE},
{0x200, 0x100, false, FilterFlag::DONT_CARE, FilterFlag::DONT_CARE},
};
const auto [result, closeHandle] = listen(filters, new CanMessageListener());

View file

@ -244,14 +244,14 @@ TEST_F(CanBusVirtualHalTest, Filter) {
auto bus2 = makeBus();
hidl_vec<CanMessageFilter> filterPositive = {
{0x101, 0x100, false},
{0x010, 0x0F0, false},
{0x101, 0x100, false, FilterFlag::DONT_CARE, FilterFlag::DONT_CARE},
{0x010, 0x0F0, false, FilterFlag::DONT_CARE, FilterFlag::DONT_CARE},
};
auto listenerPositive = bus2.listen(filterPositive);
hidl_vec<CanMessageFilter> filterNegative = {
{0x123, 0x0FF, true},
{0x004, 0x00F, true},
{0x123, 0x0FF, true, FilterFlag::DONT_CARE, FilterFlag::DONT_CARE},
{0x004, 0x00F, true, FilterFlag::DONT_CARE, FilterFlag::DONT_CARE},
};
auto listenerNegative = bus2.listen(filterNegative);