Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
71dcc34
eliminate brands based on ECUs that respond to tester present
gregjhogan Dec 26, 2021
43bc6a6
make it work
gregjhogan Dec 26, 2021
5d0a470
Add type hint for can message
sshane Jun 1, 2022
ad40d47
Only query for addresses in fingerprints, and account for different b…
sshane Jun 1, 2022
55b837a
These need to be addresses, not response addresses
sshane Jun 1, 2022
8e54436
We need to listen to response addresses, not query addresses
sshane Jun 1, 2022
e981add
add to files_common
sshane Jun 1, 2022
03ec08c
Unused Optional
sshane Jun 1, 2022
10d68a0
add logging
sshane Jun 1, 2022
250a89c
only query essential ecus
sshane Jun 2, 2022
6fea1b1
Merge remote-tracking branch 'upstream/master' into fast-fw-fp
sshane Jun 4, 2022
2ee55ae
simplify get_brand_candidates(), keep track of multiple request varia…
sshane Jun 6, 2022
5793a1e
fixes
sshane Jun 6, 2022
c4ffd70
(addr, subaddr, bus) can be common across brands, add a match to each…
sshane Jun 6, 2022
c445b98
fix length
sshane Jun 6, 2022
48e9651
query subaddrs in sequence
sshane Jun 7, 2022
a240774
fix
sshane Jun 7, 2022
9cad5c2
candidate if a platform is a subset of responding ecu addresses
sshane Jun 7, 2022
08d2058
Merge remote-tracking branch 'upstream/master' into fast-fw-fp
sshane Jun 16, 2022
5a77da3
do logging for shadow mode
sshane Jun 16, 2022
e539787
Merge remote-tracking branch 'upstream/master' into fast-fw-fp
sshane Jun 20, 2022
947f9de
Merge remote-tracking branch 'upstream/master' into fast-fw-fp
sshane Jul 5, 2022
dee85a4
query fw using most likely brands first, break when match
sshane Jul 5, 2022
82f6012
fix crash
sshane Jul 6, 2022
ff782b1
Merge remote-tracking branch 'upstream/master' into fast-fw-fp
sshane Jul 7, 2022
0e09112
Merge remote-tracking branch 'upstream/master' into fast-fw-fp
sshane Jul 8, 2022
9abf7f9
Merge remote-tracking branch 'upstream/master' into fast-fw-fp
sshane Jul 8, 2022
7a1a237
clean up
sshane Jul 8, 2022
09d0c7f
add brand filtering for debug script
sshane Jul 8, 2022
ac97188
no cache
sshane Jul 8, 2022
f87b809
fix that
sshane Jul 8, 2022
4a96ad3
fixes
sshane Jul 8, 2022
3a2b635
same name
sshane Jul 8, 2022
0ea9749
this likely isn't needed, we vin and get tester present responses bef…
sshane Jul 8, 2022
b53be8b
cache
sshane Jul 8, 2022
a742300
fix type annotation
sshane Jul 8, 2022
345bef9
no enumerate
sshane Jul 8, 2022
a969049
fix
sshane Jul 8, 2022
e2c5109
Merge remote-tracking branch 'upstream/master' into fast-fw-fp
sshane Jul 8, 2022
420e173
make sure matching function checks for multiple matches across brands
sshane Jul 8, 2022
15c57a3
Merge remote-tracking branch 'upstream/master' into fast-fw-fp
sshane Jul 8, 2022
428581c
this can be the same again
sshane Jul 8, 2022
3976923
Merge remote-tracking branch 'upstream/master' into fast-fw-fp
sshane Jul 9, 2022
4651349
Merge remote-tracking branch 'upstream/master' into fast-fw-fp
sshane Jul 9, 2022
ab03234
Merge remote-tracking branch 'upstream/master' into fast-fw-fp
sshane Jul 9, 2022
b87c3a0
clean up some functions/names
sshane Jul 9, 2022
af9ac2e
self explanetory with rework
sshane Jul 9, 2022
572f1b7
names
sshane Jul 9, 2022
f6936cc
better ordering of func vars, fix typing
sshane Jul 9, 2022
2b311cc
better yet
sshane Jul 9, 2022
27eeb6e
unused dict
sshane Jul 9, 2022
3720950
more accurate comment
sshane Jul 9, 2022
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
1 change: 1 addition & 0 deletions release/files_common
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ selfdrive/car/interfaces.py
selfdrive/car/vin.py
selfdrive/car/disable_ecu.py
selfdrive/car/fw_versions.py
selfdrive/car/ecu_addrs.py
selfdrive/car/isotp_parallel_query.py
selfdrive/car/tests/__init__.py
selfdrive/car/tests/test_car_interfaces.py
Expand Down
76 changes: 76 additions & 0 deletions selfdrive/car/ecu_addrs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env python3
import capnp
import time
import traceback
from typing import Dict, Set

import cereal.messaging as messaging
from panda.python.uds import SERVICE_TYPE
from selfdrive.car import make_can_msg
from selfdrive.boardd.boardd import can_list_to_can_capnp
from selfdrive.swaglog import cloudlog

TESTER_PRESENT_DAT = bytes([0x02, SERVICE_TYPE.TESTER_PRESENT, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0])


def is_tester_present_response(msg: capnp.lib.capnp._DynamicStructReader) -> bool:
# ISO-TP messages are always padded to 8 bytes
# tester present response is always a single frame
if len(msg.dat) == 8 and 1 <= msg.dat[0] <= 7:
# success response
if msg.dat[1] == (SERVICE_TYPE.TESTER_PRESENT + 0x40):
return True
# error response
if msg.dat[1] == 0x7F and msg.dat[2] == SERVICE_TYPE.TESTER_PRESENT:
return True
return False


def get_all_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, bus: int, timeout: float = 1, debug: bool = True) -> Set[int]:
addr_list = [0x700 + i for i in range(256)] + [0x18da00f1 + (i << 8) for i in range(256)]
query_addrs = response_addrs = {addr: bus for addr in addr_list}
return get_ecu_addrs(logcan, sendcan, query_addrs, response_addrs, timeout=timeout, debug=debug)


def get_ecu_addrs(logcan: messaging.SubSocket, sendcan: messaging.PubSocket, query_addrs: Dict[int, int], response_addrs: Dict[int, int], timeout: float = 1, debug: bool = True) -> Set[int]:
ecu_addrs = set()
try:
msgs = [make_can_msg(addr, TESTER_PRESENT_DAT, bus) for addr, bus in query_addrs.items()]

messaging.drain_sock_raw(logcan)
sendcan.send(can_list_to_can_capnp(msgs, msgtype='sendcan'))
start_time = time.monotonic()
while time.monotonic() - start_time < timeout:
can_packets = messaging.drain_sock(logcan, wait_for_one=True)
for packet in can_packets:
for msg in packet.can:
if msg.address in response_addrs and msg.src == response_addrs[msg.address] and is_tester_present_response(msg):
if debug:
print(f"CAN-RX: {hex(msg.address)} - 0x{bytes.hex(msg.dat)}")
if msg.address in ecu_addrs:
print(f"Duplicate ECU address: {hex(msg.address)}")
ecu_addrs.add(msg.address)
except Exception:
cloudlog.warning(f"ECU addr scan exception: {traceback.format_exc()}")
return ecu_addrs


if __name__ == "__main__":
import argparse

parser = argparse.ArgumentParser(description='Get addresses of all ECUs')
parser.add_argument('--debug', action='store_true')
args = parser.parse_args()

logcan = messaging.sub_sock('can')
sendcan = messaging.pub_sock('sendcan')

time.sleep(1.0)

print("Getting ECU addresses ...")
ecu_addrs = get_all_ecu_addrs(logcan, sendcan, 1, debug=args.debug)

print()
print("Found ECUs on addresses:")
for addr in ecu_addrs:
print(f" {hex(addr)}")
42 changes: 40 additions & 2 deletions selfdrive/car/fw_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@

import panda.python.uds as uds
from cereal import car
from selfdrive.car.ecu_addrs import get_ecu_addrs
from selfdrive.car.interfaces import get_interface_attr
from selfdrive.car.fingerprints import FW_VERSIONS
from selfdrive.car.isotp_parallel_query import IsoTpParallelQuery
from selfdrive.car.toyota.values import CAR as TOYOTA
from selfdrive.swaglog import cloudlog

Ecu = car.CarParams.Ecu
ESSENTIAL_ECUS = [Ecu.engine, Ecu.eps, Ecu.esp, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.vsa]


def p16(val):
Expand Down Expand Up @@ -259,7 +261,6 @@ def match_fw_to_car_exact(fw_versions_dict):
ecu_type = ecu[0]
addr = ecu[1:]
found_version = fw_versions_dict.get(addr, None)
ESSENTIAL_ECUS = [Ecu.engine, Ecu.eps, Ecu.esp, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.vsa]
if ecu_type == Ecu.esp and candidate in (TOYOTA.RAV4, TOYOTA.COROLLA, TOYOTA.HIGHLANDER, TOYOTA.SIENNA, TOYOTA.LEXUS_IS) and found_version is None:
continue

Expand Down Expand Up @@ -296,20 +297,57 @@ def match_fw_to_car(fw_versions, allow_fuzzy=True):

return exact_match, matches

def get_brand_candidates(logcan, sendcan, versions):
response_offset_bus_by_brand = dict()
for r in REQUESTS:
# TODO: some brands have multiple response offsets
response_offset_bus_by_brand[r.brand] = (r.rx_offset, r.bus)

# maps query and response addresses to their request's bus
query_addrs_by_brand = defaultdict(dict)
response_addrs_by_brand = defaultdict(dict)
for brand, brand_versions in versions.items():
if brand not in response_offset_bus_by_brand:
continue

for c in brand_versions.values():
for ecu, addr, _ in c.keys():
# reduce matches with ecus not critical to fingerprinting
if ecu not in ESSENTIAL_ECUS:
continue

response_offset, bus = response_offset_bus_by_brand[brand]
query_addrs_by_brand[brand][addr] = bus
response_addrs_by_brand[brand][addr + response_offset] = bus

query_addrs = dict(addr for addr_bus in query_addrs_by_brand.values() for addr in addr_bus.items())
response_addrs = dict(addr for addr_bus in response_addrs_by_brand.values() for addr in addr_bus.items())
ecu_response_addrs = get_ecu_addrs(logcan, sendcan, query_addrs, response_addrs)
cloudlog.event("ecu response addrs", ecu_response_addrs=ecu_response_addrs)

brand_candidates = list()
for brand, brand_addrs in response_addrs_by_brand.items():
cloudlog.event("candidate brand intersection", brand=brand, intersection=set(brand_addrs).intersection(ecu_response_addrs))
if len(set(brand_addrs).intersection(ecu_response_addrs)) >= 4:
brand_candidates.append(brand)
return brand_candidates

def get_fw_versions(logcan, sendcan, extra=None, timeout=0.1, debug=False, progress=False):
ecu_types = {}

# Extract ECU addresses to query from fingerprints
# ECUs using a subadress need be queried one by one, the rest can be done in parallel
# ECUs using a subaddress need be queried one by one, the rest can be done in parallel
addrs = []
parallel_addrs = []

versions = get_interface_attr('FW_VERSIONS', ignore_none=True)
if extra is not None:
versions.update(extra)

brand_candidates = get_brand_candidates(logcan, sendcan, versions)
for brand, brand_versions in versions.items():
if brand_candidates and brand not in brand_candidates:
continue
for c in brand_versions.values():
for ecu_type, addr, sub_addr in c.keys():
a = (brand, addr, sub_addr)
Expand Down