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:
parent
f72f955b4a
commit
bee3d2c3c9
7 changed files with 122 additions and 23 deletions
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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);
|
||||
if (message.remoteTransmissionRequest) {
|
||||
std::cout << " RTR";
|
||||
} else {
|
||||
std::cout << " [" << message.payload.size() << "] ";
|
||||
if (message.remoteTransmissionRequest) {
|
||||
std::cout << "remote request";
|
||||
} else {
|
||||
for (const auto byte : message.payload) {
|
||||
std::cout << " " << std::setfill('0') << std::setw(2) << unsigned(byte);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue