Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add core OP support for CRC validation, with extra support for Volkswagen MQB #836

Merged
merged 21 commits into from
Oct 17, 2019
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1c86ca2
Merge pull request #1 from commaai/devel
jyoung8607 Mar 27, 2019
72cb204
Merge pull request #12 from commaai/devel
jyoung8607 Apr 26, 2019
d1f1310
Merge pull request #15 from commaai/devel
jyoung8607 Jul 24, 2019
8e961ed
Merge pull request #18 from commaai/devel
jyoung8607 Aug 1, 2019
28e7880
Merge branch 'devel' of https://github.com/commaai/openpilot into devel
jyoung8607 Aug 6, 2019
809cbfd
Merge remote-tracking branch 'CommaAI/devel' into devel
jyoung8607 Oct 2, 2019
799302b
Generalized core OP CRC support plus extra bits for Volkswagen MQB.
jyoung8607 Oct 9, 2019
a93a7e8
Add 2018 Civic Hatchback 1.0l CVT (European) support (#823)
csouers Oct 3, 2019
2572ce3
Squashed 'cereal/' changes from ea14abe4b..3d90c7877
Oct 9, 2019
0c48316
Squashed 'panda/' changes from 9881e6118..30c7ca8a5
Oct 9, 2019
213a18b
Squashed 'opendbc/' changes from 684e28a7a..f3b573559
Oct 9, 2019
c8ff06c
openpilot v0.6.5 release
Oct 9, 2019
5c9648a
Merge remote-tracking branch 'CommaAI/devel' into devel
jyoung8607 Oct 12, 2019
603b832
Temp CRC->CHECKSUM renaming, refactor DBC preprocessor.
jyoung8607 Oct 17, 2019
1199025
Merge branch 'devel' into vw-064-crc-upstream
jyoung8607 Oct 17, 2019
887e5d5
Merge branch 'devel' into vw-064-crc-upstream
jyoung8607 Oct 17, 2019
92c3cec
Merge remote-tracking branch 'origin/vw-064-crc-upstream' into vw-064…
jyoung8607 Oct 17, 2019
ecd904c
EV_Gearshift message CRC support for e-Golf
jyoung8607 Oct 17, 2019
a391a58
Merge Accord Touring fix so Travis can build, delete extra files.
jyoung8607 Oct 17, 2019
f5eddf2
Tweaking the validation approach/code based on DM with @rbiasini.
jyoung8607 Oct 17, 2019
5a0748c
Getting tricky with tuples!
jyoung8607 Oct 17, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion selfdrive/can/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@

#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))


unsigned int honda_checksum(unsigned int address, uint64_t d, int l);
unsigned int toyota_checksum(unsigned int address, uint64_t d, int l);
unsigned int pedal_checksum(unsigned int address, uint64_t d, int l);

void init_crc_lookup_tables();
unsigned int volkswagen_crc(unsigned int address, uint64_t d, int l);

struct SignalPackValue {
const char* name;
double value;
Expand Down Expand Up @@ -44,6 +46,8 @@ enum SignalType {
TOYOTA_CHECKSUM,
PEDAL_CHECKSUM,
PEDAL_COUNTER,
VOLKSWAGEN_CRC,
VOLKSWAGEN_COUNTER,
};

struct Signal {
Expand Down
4 changes: 4 additions & 0 deletions selfdrive/can/dbc_template.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ const Signal sigs_{{address}}[] = {
.type = SignalType::PEDAL_CHECKSUM,
{% elif address in [512, 513] and sig.name == "COUNTER_PEDAL" %}
.type = SignalType::PEDAL_COUNTER,
{% elif checksum_type == "volkswagen" and sig.name == "CRC" %}
.type = SignalType::VOLKSWAGEN_CRC,
{% elif checksum_type == "volkswagen" and sig.name == "COUNTER" %}
.type = SignalType::VOLKSWAGEN_COUNTER,
{% else %}
.type = SignalType::DEFAULT,
{% endif %}
Expand Down
2 changes: 2 additions & 0 deletions selfdrive/can/libdbc_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
TOYOTA_CHECKSUM,
PEDAL_CHECKSUM,
PEDAL_COUNTER,
VOLKSWAGEN_CRC,
VOLKSWAGEN_COUNTER,
} SignalType;

typedef struct {
Expand Down
28 changes: 22 additions & 6 deletions selfdrive/can/packer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ namespace {
signal_lookup[std::make_pair(msg->address, std::string(sig->name))] = *sig;
}
}

init_crc_lookup_tables();
}

uint64_t pack(uint32_t address, const std::vector<SignalPackValue> &signals, int counter) {
Expand Down Expand Up @@ -82,28 +84,42 @@ namespace {
}
auto sig = sig_it->second;

if (sig.type != SignalType::HONDA_COUNTER){
if ((sig.type != SignalType::HONDA_COUNTER) && (sig.type != SignalType::VOLKSWAGEN_COUNTER)) {
WARN("COUNTER signal type not valid\n");
}

ret = set_value(ret, sig, counter);
}

auto sig_it = signal_lookup.find(std::make_pair(address, "CHECKSUM"));
if (sig_it != signal_lookup.end()) {
auto sig = sig_it->second;
if (sig.type == SignalType::HONDA_CHECKSUM){
auto sig_it_checksum = signal_lookup.find(std::make_pair(address, "CHECKSUM"));
if (sig_it_checksum != signal_lookup.end()) {
auto sig = sig_it_checksum->second;
if (sig.type == SignalType::HONDA_CHECKSUM) {
unsigned int chksm = honda_checksum(address, ret, message_lookup[address].size);
ret = set_value(ret, sig, chksm);
}
else if (sig.type == SignalType::TOYOTA_CHECKSUM){
else if (sig.type == SignalType::TOYOTA_CHECKSUM) {
unsigned int chksm = toyota_checksum(address, ret, message_lookup[address].size);
ret = set_value(ret, sig, chksm);
} else {
//WARN("CHECKSUM signal type not valid\n");
}
}

auto sig_it_crc = signal_lookup.find(std::make_pair(address, "CRC"));
if (sig_it_crc != signal_lookup.end()) {
auto sig = sig_it_crc->second;
if (sig.type == SignalType::VOLKSWAGEN_CRC) {
// FIXME: Hackish fix for an endianness issue. The message is in reverse byte order
// until later in the pack process. Checksums can be run backwards, CRCs not so much.
// The correct fix is unclear but this works for the moment.
unsigned int chksm = volkswagen_crc(address, ReverseBytes(ret), message_lookup[address].size);
ret = set_value(ret, sig, chksm);
} else {
// WARN("CRC signal type not valid\n");
}
}

return ret;
}

Expand Down
4 changes: 3 additions & 1 deletion selfdrive/can/packer_impl.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ ctypedef enum SignalType:
HONDA_COUNTER,
TOYOTA_CHECKSUM,
PEDAL_CHECKSUM,
PEDAL_COUNTER
PEDAL_COUNTER,
VOLKSWAGEN_CRC,
VOLKSWAGEN_COUNTER

cdef struct Signal:
const char* name
Expand Down
114 changes: 108 additions & 6 deletions selfdrive/can/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@
// #define DEBUG printf
#define INFO printf


#define MAX_BAD_COUNTER 5

// Static lookup table for fast computation of CRC8 poly 0x2F, aka 8H2F/AUTOSAR
uint8_t crc8_lut_8h2f[256];

unsigned int honda_checksum(unsigned int address, uint64_t d, int l) {
d >>= ((8-l)*8); // remove padding
d >>= 4; // remove checksum
Expand Down Expand Up @@ -75,6 +77,95 @@ unsigned int pedal_checksum(unsigned int address, uint64_t d, int l) {
return crc;
}

void gen_crc_lookup_table(uint8_t poly, uint8_t crc_lut[])
{
uint8_t crc;
int i, j;

for (i = 0; i < 256; i++) {
crc = i;
for (j = 0; j < 8; j++) {
if ((crc & 0x80) != 0)
crc = (uint8_t)((crc << 1) ^ poly);
else
crc <<= 1;
}
crc_lut[i] = crc;
}
}

void init_crc_lookup_tables()
{
// At init time, set up static lookup tables for fast CRC computation.

gen_crc_lookup_table(0x2F, crc8_lut_8h2f); // CRC-8 8H2F/AUTOSAR for Volkswagen
}

unsigned int volkswagen_crc(unsigned int address, uint64_t d, int l)
{
// Volkswagen uses standard CRC8 8H2F/AUTOSAR, but they compute it with
// a magic variable padding byte tacked onto the end of the payload.
// https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_CRCLibrary.pdf

uint8_t *dat = (uint8_t *)&d;
uint8_t crc = 0xFF; // Standard init value for CRC8 8H2F/AUTOSAR

// CRC the payload first, skipping over the first byte where the CRC lives.
for (int i = 1; i < l; i++) {
crc ^= dat[i];
crc = crc8_lut_8h2f[crc];
}

// Look up and apply the magic final CRC padding byte, which permutes by CAN
// address, and additionally (for SOME addresses) by the message counter.
uint8_t counter = dat[1] & 0x0F;
switch(address) {
case 0x86: // LWI_01 Steering Angle
crc ^= (uint8_t[]){0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86}[counter];
break;
case 0x9F: // EPS_01 Electric Power Steering
crc ^= (uint8_t[]){0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5}[counter];
break;
case 0xAD: // Getriebe_11 Automatic Gearbox
crc ^= (uint8_t[]){0x3F,0x69,0x39,0xDC,0x94,0xF9,0x14,0x64,0xD8,0x6A,0x34,0xCE,0xA2,0x55,0xB5,0x2C}[counter];
break;
case 0xFD: // ESP_21 Electronic Stability Program
crc ^= (uint8_t[]){0xB4,0xEF,0xF8,0x49,0x1E,0xE5,0xC2,0xC0,0x97,0x19,0x3C,0xC9,0xF1,0x98,0xD6,0x61}[counter];
break;
case 0x106: // ESP_05 Electronic Stability Program
crc ^= (uint8_t[]){0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07}[counter];
break;
case 0x117: // ACC_10 Automatic Cruise Control
crc ^= (uint8_t[]){0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC,0xAC}[counter];
break;
case 0x122: // ACC_06 Automatic Cruise Control
crc ^= (uint8_t[]){0x37,0x7D,0xF3,0xA9,0x18,0x46,0x6D,0x4D,0x3D,0x71,0x92,0x9C,0xE5,0x32,0x10,0xB9}[counter];
break;
case 0x126: // HCA_01 Heading Control Assist
crc ^= (uint8_t[]){0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA,0xDA}[counter];
break;
case 0x12B: // GRA_ACC_01 Steering wheel controls for ACC
crc ^= (uint8_t[]){0x6A,0x38,0xB4,0x27,0x22,0xEF,0xE1,0xBB,0xF8,0x80,0x84,0x49,0xC7,0x9E,0x1E,0x2B}[counter];
break;
case 0x30C: // ACC_02 Automatic Cruise Control
crc ^= (uint8_t[]){0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F}[counter];
break;
case 0x3C0: // Klemmen_Status_01 ignition and starting status
crc ^= (uint8_t[]){0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3}[counter];
break;
case 0x65D: // ESP_20 Electronic Stability Program
crc ^= (uint8_t[]){0xAC,0xB3,0xAB,0xEB,0x7A,0xE1,0x3B,0xF7,0x73,0xBA,0x7C,0x9E,0x06,0x5F,0x02,0xD9}[counter];
break;
default: // As-yet undefined CAN message, CRC check expected to fail
INFO("Attempt to CRC check undefined Volkswagen message 0x%02X\n", address);
crc ^= (uint8_t[]){0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}[counter];
break;
}
crc = crc8_lut_8h2f[crc];

return crc ^ 0xFF; // Return after standard final XOR for CRC8 8H2F/AUTOSAR
}

namespace {

uint64_t read_u64_be(const uint8_t* v) {
Expand Down Expand Up @@ -133,7 +224,7 @@ struct MessageState {

if (sig.type == SignalType::HONDA_CHECKSUM) {
if (honda_checksum(address, dat, size) != tmp) {
INFO("%X CHECKSUM FAIL\n", address);
INFO("0x%X CHECKSUM FAIL\n", address);
return false;
}
} else if (sig.type == SignalType::HONDA_COUNTER) {
Expand All @@ -142,12 +233,21 @@ struct MessageState {
}
} else if (sig.type == SignalType::TOYOTA_CHECKSUM) {
if (toyota_checksum(address, dat, size) != tmp) {
INFO("%X CHECKSUM FAIL\n", address);
INFO("0x%X CHECKSUM FAIL\n", address);
return false;
}
} else if (sig.type == SignalType::VOLKSWAGEN_CRC) {
if (volkswagen_crc(address, dat, size) != tmp) {
INFO("0x%X CRC FAIL\n", address);
return false;
}
} else if (sig.type == SignalType::VOLKSWAGEN_COUNTER) {
if (!update_counter_generic(tmp, sig.b2)) {
return false;
}
} else if (sig.type == SignalType::PEDAL_CHECKSUM) {
if (pedal_checksum(address, dat, size) != tmp) {
INFO("%X PEDAL CHECKSUM FAIL\n", address);
INFO("0x%X PEDAL CHECKSUM FAIL\n", address);
return false;
}
} else if (sig.type == SignalType::PEDAL_COUNTER) {
Expand All @@ -171,7 +271,7 @@ struct MessageState {
if (((old_counter+1) & ((1 << cnt_size) -1)) != v) {
counter_fail += 1;
if (counter_fail > 1) {
INFO("%X COUNTER FAIL %d -- %d vs %d\n", address, counter_fail, old_counter, (int)v);
INFO("0x%X COUNTER FAIL %d -- %d vs %d\n", address, counter_fail, old_counter, (int)v);
}
if (counter_fail >= MAX_BAD_COUNTER) {
return false;
Expand Down Expand Up @@ -223,7 +323,9 @@ class CANParser {
}

dbc = dbc_lookup(dbc_name);
assert(dbc);
assert(dbc);
init_crc_lookup_tables();

for (const auto& op : options) {
MessageState state = {
.address = op.address,
Expand Down
4 changes: 3 additions & 1 deletion selfdrive/can/parser_pyx.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ ctypedef enum SignalType:
HONDA_COUNTER,
TOYOTA_CHECKSUM,
PEDAL_CHECKSUM,
PEDAL_COUNTER
PEDAL_COUNTER,
VOLKSWAGEN_CRC,
VOLKSWAGEN_COUNTER

cdef struct Signal:
const char* name
Expand Down
54 changes: 35 additions & 19 deletions selfdrive/can/process_dbc.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,48 +38,64 @@ def main():
if dbc_mtime < out_mtime and template_mtime < out_mtime and this_file_mtime < out_mtime:
continue #skip output is newer than template and dbc

msgs = [(address, msg_name, msg_size, sorted(msg_sigs, key=lambda s: s.name not in ("COUNTER", "CHECKSUM"))) # process counter and checksums first
msgs = [(address, msg_name, msg_size, sorted(msg_sigs, key=lambda s: s.name not in ("COUNTER", "CHECKSUM", "CRC"))) # process counter and checksums first
for address, ((msg_name, msg_size), msg_sigs) in sorted(can_dbc.msgs.items()) if msg_sigs]

def_vals = {a: set(b) for a,b in can_dbc.def_vals.items()} #remove duplicates
def_vals = [(address, sig) for address, sig in sorted(def_vals.items())]

if can_dbc.name.startswith("honda") or can_dbc.name.startswith("acura"):
checksum_type = "honda"
car_type = "honda"
checksum_size = 4
counter_size = 2
elif can_dbc.name.startswith("toyota") or can_dbc.name.startswith("lexus"):
checksum_type = "toyota"
car_type = "toyota"
checksum_size = 8
counter_size = None
elif can_dbc.name.startswith("vw") or can_dbc.name.startswith("volkswagen") or can_dbc.name.startswith("audi") or can_dbc.name.startswith ("seat") or can_dbc.name.startswith("skoda"):
car_type = "volkswagen"
checksum_size = 8
counter_size = 4
else:
checksum_type = None
car_type = None
checksum_size = None
counter_size = None
jyoung8607 marked this conversation as resolved.
Show resolved Hide resolved

for address, msg_name, msg_size, sigs in msgs:
for sig in sigs:
if checksum_type is not None and sig.name == "CHECKSUM":
if sig.size != checksum_size:
sys.exit("CHECKSUM is not %d bits longs %s" % (checksum_size, msg_name))
if checksum_type == "honda" and sig.start_bit % 8 != 3:
sys.exit("CHECKSUM starts at wrong bit %s" % msg_name)
if checksum_type == "toyota" and sig.start_bit % 8 != 7:
sys.exit("CHECKSUM starts at wrong bit %s" % msg_name)
if checksum_type == "honda" and sig.name == "COUNTER":
if sig.size != 2:
sys.exit("COUNTER is not 2 bits longs %s" % msg_name)
if sig.start_bit % 8 != 5:
sys.exit("COUNTER starts at wrong bit %s" % msg_name)
if car_type is not None:
if sig.name == "CHECKSUM":
if sig.size != checksum_size:
sys.exit("CHECKSUM is not %d bits long %s" % (checksum_size, msg_name))
if car_type == "honda" and sig.start_bit % 8 != 3:
sys.exit("CHECKSUM starts at wrong bit %s" % msg_name)
if car_type == "toyota" and sig.start_bit % 8 != 7:
sys.exit("CHECKSUM starts at wrong bit %s" % msg_name)
elif sig.name == "CRC":
if sig.size != checksum_size:
sys.exit("CRC is not %d bits long %s" % (checksum_size, msg_name))
if car_type == "volkswagen" and sig.start_bit % 8 != 0:
sys.exit("CRC starts at wrong bit %s" % msg_name)
elif sig.name == "COUNTER" and car_type in ["honda", "volkswagen"]:
if sig.size != counter_size:
sys.exit("COUNTER is not %d bits long %s" % (counter_size, msg_name))
if car_type == "honda" and sig.start_bit % 8 != 5:
sys.exit("COUNTER starts at wrong bit %s" % msg_name)
if car_type == "volkswagen" and sig.start_bit % 8 != 0:
sys.exit("COUNTER starts at wrong bit %s" % msg_name)
if address in [0x200, 0x201]:
if sig.name == "COUNTER_PEDAL" and sig.size != 4:
sys.exit("PEDAL COUNTER is not 4 bits longs %s" % msg_name)
sys.exit("PEDAL COUNTER is not 4 bits long %s" % msg_name)
if sig.name == "CHECKSUM_PEDAL" and sig.size != 8:
sys.exit("PEDAL CHECKSUM is not 8 bits longs %s" % msg_name)
sys.exit("PEDAL CHECKSUM is not 8 bits long %s" % msg_name)

# Fail on duplicate message names
c = Counter([msg_name for address, msg_name, msg_size, sigs in msgs])
for name, count in c.items():
if count > 1:
sys.exit("Duplicate message name in DBC file %s" % name)

parser_code = template.render(dbc=can_dbc, checksum_type=checksum_type, msgs=msgs, def_vals=def_vals, len=len)
parser_code = template.render(dbc=can_dbc, checksum_type=car_type, msgs=msgs, def_vals=def_vals, len=len)


with open(out_fn, "w") as out_f:
Expand Down