dragonpilot - 基於 openpilot 的開源駕駛輔助系統
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

546 lines
19 KiB

5 years ago
#!/usr/bin/env python3
import struct
import traceback
from collections import defaultdict
from dataclasses import dataclass, field
from typing import Any, List, Optional, Set, Tuple
5 years ago
from tqdm import tqdm
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
5 years ago
from selfdrive.car.isotp_parallel_query import IsoTpParallelQuery
from selfdrive.car.toyota.values import CAR as TOYOTA
from system.swaglog import cloudlog
5 years ago
Ecu = car.CarParams.Ecu
ESSENTIAL_ECUS = [Ecu.engine, Ecu.eps, Ecu.esp, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.vsa]
5 years ago
5 years ago
def p16(val):
return struct.pack("!H", val)
5 years ago
TESTER_PRESENT_REQUEST = bytes([uds.SERVICE_TYPE.TESTER_PRESENT, 0x0])
TESTER_PRESENT_RESPONSE = bytes([uds.SERVICE_TYPE.TESTER_PRESENT + 0x40, 0x0])
SHORT_TESTER_PRESENT_REQUEST = bytes([uds.SERVICE_TYPE.TESTER_PRESENT])
SHORT_TESTER_PRESENT_RESPONSE = bytes([uds.SERVICE_TYPE.TESTER_PRESENT + 0x40])
DEFAULT_DIAGNOSTIC_REQUEST = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL,
uds.SESSION_TYPE.DEFAULT])
DEFAULT_DIAGNOSTIC_RESPONSE = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL + 0x40,
uds.SESSION_TYPE.DEFAULT, 0x0, 0x32, 0x1, 0xf4])
EXTENDED_DIAGNOSTIC_REQUEST = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL,
uds.SESSION_TYPE.EXTENDED_DIAGNOSTIC])
EXTENDED_DIAGNOSTIC_RESPONSE = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL + 0x40,
uds.SESSION_TYPE.EXTENDED_DIAGNOSTIC, 0x0, 0x32, 0x1, 0xf4])
UDS_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION)
UDS_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION)
HYUNDAI_VERSION_REQUEST_LONG = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(0xf100) # Long description
HYUNDAI_VERSION_REQUEST_MULTI = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_SPARE_PART_NUMBER) + \
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION) + \
p16(0xf100)
HYUNDAI_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40])
5 years ago
TOYOTA_VERSION_REQUEST = b'\x1a\x88\x01'
TOYOTA_VERSION_RESPONSE = b'\x5a\x88\x01'
VOLKSWAGEN_VERSION_REQUEST_MULTI = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_SPARE_PART_NUMBER) + \
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_ECU_SOFTWARE_VERSION_NUMBER) + \
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
VOLKSWAGEN_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40])
5 years ago
OBD_VERSION_REQUEST = b'\x09\x04'
OBD_VERSION_RESPONSE = b'\x49\x04'
DEFAULT_RX_OFFSET = 0x8
VOLKSWAGEN_RX_OFFSET = 0x6a
5 years ago
MAZDA_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_ECU_SOFTWARE_NUMBER)
MAZDA_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_ECU_SOFTWARE_NUMBER)
Fingerprinting v2 support for Nissan vehicles (#22655) * Add Nissan FPv2 * Update Nissan version response * Add second Nissan FPv2 query * Add multiple offsets for Nissan * Nissan offset fix * Add Nissan diagnostic request * Use 21 83 for Nissan version request * Add partial 2018 Leaf FPv2 * Update 2018 Leaf FPv2 * Nissan FPv2: more RX offsets, add no diagostic request scan for 0x20 * Add FPv2 imports to Nissan values * Change LEAF to LEAF_IC It is an instrument cluster leaf. With this minor change it works :). * fixed ecu addr for nissan eps * Remove FW not used for fpv2 * Remove unused offsets * add fw versions for 2018 Nissan Leaf JP * add standard ecu fw query * add esp fw * add nissan xtrail * Add missing ESP FW for 2018 Nissan Leaf IC * Add fwdCamera FW for Xtrail 2017 * add missing comma * Add 0x8 default offset needed e.g. for xtrail engine ecu * Apply suggestions from code review Co-authored-by: Willem Melching <willem.melching@gmail.com> * Fix variable naming * remove unnecessary part of standard fw query * add fw versions for CAN-GW ECU * transmission eco does not reliable answer -> remove * add Altima FWs (part 1) * add Altima EPS FW * Remove NISSAN_KWP_DIAGNOSTIC_REQUEST and cleanup * Revert "Remove NISSAN_KWP_DIAGNOSTIC_REQUEST and cleanup" This reverts commit 808d6f1562e9a6cb57ea365d184b74d51f6da017. * cleanup Co-authored-by: Martin Lillepuu <martin@mlp.ee> Co-authored-by: Julian Pieles <julian@pieles.digital> Co-authored-by: Willem Melching <willem.melching@gmail.com>
4 years ago
NISSAN_DIAGNOSTIC_REQUEST_KWP = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL, 0xc0])
NISSAN_DIAGNOSTIC_RESPONSE_KWP = bytes([uds.SERVICE_TYPE.DIAGNOSTIC_SESSION_CONTROL + 0x40, 0xc0])
NISSAN_VERSION_REQUEST_KWP = b'\x21\x83'
NISSAN_VERSION_RESPONSE_KWP = b'\x61\x83'
NISSAN_VERSION_REQUEST_STANDARD = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_ECU_SOFTWARE_NUMBER)
NISSAN_VERSION_RESPONSE_STANDARD = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_ECU_SOFTWARE_NUMBER)
NISSAN_RX_OFFSET = 0x20
SUBARU_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
SUBARU_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
CHRYSLER_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(0xf132)
CHRYSLER_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
p16(0xf132)
CHRYSLER_RX_OFFSET = -0x280
FORD_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_ECU_SOFTWARE_NUMBER)
FORD_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + \
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_ECU_SOFTWARE_NUMBER)
@dataclass
class Request:
brand: str
request: List[bytes]
response: List[bytes]
whitelist_ecus: List[int] = field(default_factory=list)
rx_offset: int = DEFAULT_RX_OFFSET
bus: int = 1
REQUESTS: List[Request] = [
# Subaru
Request(
"subaru",
[TESTER_PRESENT_REQUEST, SUBARU_VERSION_REQUEST],
[TESTER_PRESENT_RESPONSE, SUBARU_VERSION_RESPONSE],
),
# Hyundai
Request(
"hyundai",
[HYUNDAI_VERSION_REQUEST_LONG],
[HYUNDAI_VERSION_RESPONSE],
),
Request(
"hyundai",
[HYUNDAI_VERSION_REQUEST_MULTI],
[HYUNDAI_VERSION_RESPONSE],
),
5 years ago
# Honda
Request(
"honda",
5 years ago
[UDS_VERSION_REQUEST],
[UDS_VERSION_RESPONSE],
5 years ago
),
# Toyota
Request(
"toyota",
5 years ago
[SHORT_TESTER_PRESENT_REQUEST, TOYOTA_VERSION_REQUEST],
[SHORT_TESTER_PRESENT_RESPONSE, TOYOTA_VERSION_RESPONSE],
bus=0,
5 years ago
),
Request(
"toyota",
5 years ago
[SHORT_TESTER_PRESENT_REQUEST, OBD_VERSION_REQUEST],
[SHORT_TESTER_PRESENT_RESPONSE, OBD_VERSION_RESPONSE],
bus=0,
5 years ago
),
Request(
"toyota",
5 years ago
[TESTER_PRESENT_REQUEST, DEFAULT_DIAGNOSTIC_REQUEST, EXTENDED_DIAGNOSTIC_REQUEST, UDS_VERSION_REQUEST],
[TESTER_PRESENT_RESPONSE, DEFAULT_DIAGNOSTIC_RESPONSE, EXTENDED_DIAGNOSTIC_RESPONSE, UDS_VERSION_RESPONSE],
bus=0,
),
# Volkswagen
Request(
"volkswagen",
[VOLKSWAGEN_VERSION_REQUEST_MULTI],
[VOLKSWAGEN_VERSION_RESPONSE],
whitelist_ecus=[Ecu.srs, Ecu.eps, Ecu.fwdRadar],
rx_offset=VOLKSWAGEN_RX_OFFSET,
),
Request(
"volkswagen",
[VOLKSWAGEN_VERSION_REQUEST_MULTI],
[VOLKSWAGEN_VERSION_RESPONSE],
whitelist_ecus=[Ecu.engine, Ecu.transmission],
),
# Mazda
Request(
"mazda",
[MAZDA_VERSION_REQUEST],
[MAZDA_VERSION_RESPONSE],
Fingerprinting v2 support for Nissan vehicles (#22655) * Add Nissan FPv2 * Update Nissan version response * Add second Nissan FPv2 query * Add multiple offsets for Nissan * Nissan offset fix * Add Nissan diagnostic request * Use 21 83 for Nissan version request * Add partial 2018 Leaf FPv2 * Update 2018 Leaf FPv2 * Nissan FPv2: more RX offsets, add no diagostic request scan for 0x20 * Add FPv2 imports to Nissan values * Change LEAF to LEAF_IC It is an instrument cluster leaf. With this minor change it works :). * fixed ecu addr for nissan eps * Remove FW not used for fpv2 * Remove unused offsets * add fw versions for 2018 Nissan Leaf JP * add standard ecu fw query * add esp fw * add nissan xtrail * Add missing ESP FW for 2018 Nissan Leaf IC * Add fwdCamera FW for Xtrail 2017 * add missing comma * Add 0x8 default offset needed e.g. for xtrail engine ecu * Apply suggestions from code review Co-authored-by: Willem Melching <willem.melching@gmail.com> * Fix variable naming * remove unnecessary part of standard fw query * add fw versions for CAN-GW ECU * transmission eco does not reliable answer -> remove * add Altima FWs (part 1) * add Altima EPS FW * Remove NISSAN_KWP_DIAGNOSTIC_REQUEST and cleanup * Revert "Remove NISSAN_KWP_DIAGNOSTIC_REQUEST and cleanup" This reverts commit 808d6f1562e9a6cb57ea365d184b74d51f6da017. * cleanup Co-authored-by: Martin Lillepuu <martin@mlp.ee> Co-authored-by: Julian Pieles <julian@pieles.digital> Co-authored-by: Willem Melching <willem.melching@gmail.com>
4 years ago
),
# Nissan
Request(
Fingerprinting v2 support for Nissan vehicles (#22655) * Add Nissan FPv2 * Update Nissan version response * Add second Nissan FPv2 query * Add multiple offsets for Nissan * Nissan offset fix * Add Nissan diagnostic request * Use 21 83 for Nissan version request * Add partial 2018 Leaf FPv2 * Update 2018 Leaf FPv2 * Nissan FPv2: more RX offsets, add no diagostic request scan for 0x20 * Add FPv2 imports to Nissan values * Change LEAF to LEAF_IC It is an instrument cluster leaf. With this minor change it works :). * fixed ecu addr for nissan eps * Remove FW not used for fpv2 * Remove unused offsets * add fw versions for 2018 Nissan Leaf JP * add standard ecu fw query * add esp fw * add nissan xtrail * Add missing ESP FW for 2018 Nissan Leaf IC * Add fwdCamera FW for Xtrail 2017 * add missing comma * Add 0x8 default offset needed e.g. for xtrail engine ecu * Apply suggestions from code review Co-authored-by: Willem Melching <willem.melching@gmail.com> * Fix variable naming * remove unnecessary part of standard fw query * add fw versions for CAN-GW ECU * transmission eco does not reliable answer -> remove * add Altima FWs (part 1) * add Altima EPS FW * Remove NISSAN_KWP_DIAGNOSTIC_REQUEST and cleanup * Revert "Remove NISSAN_KWP_DIAGNOSTIC_REQUEST and cleanup" This reverts commit 808d6f1562e9a6cb57ea365d184b74d51f6da017. * cleanup Co-authored-by: Martin Lillepuu <martin@mlp.ee> Co-authored-by: Julian Pieles <julian@pieles.digital> Co-authored-by: Willem Melching <willem.melching@gmail.com>
4 years ago
"nissan",
[NISSAN_DIAGNOSTIC_REQUEST_KWP, NISSAN_VERSION_REQUEST_KWP],
[NISSAN_DIAGNOSTIC_RESPONSE_KWP, NISSAN_VERSION_RESPONSE_KWP],
),
Request(
Fingerprinting v2 support for Nissan vehicles (#22655) * Add Nissan FPv2 * Update Nissan version response * Add second Nissan FPv2 query * Add multiple offsets for Nissan * Nissan offset fix * Add Nissan diagnostic request * Use 21 83 for Nissan version request * Add partial 2018 Leaf FPv2 * Update 2018 Leaf FPv2 * Nissan FPv2: more RX offsets, add no diagostic request scan for 0x20 * Add FPv2 imports to Nissan values * Change LEAF to LEAF_IC It is an instrument cluster leaf. With this minor change it works :). * fixed ecu addr for nissan eps * Remove FW not used for fpv2 * Remove unused offsets * add fw versions for 2018 Nissan Leaf JP * add standard ecu fw query * add esp fw * add nissan xtrail * Add missing ESP FW for 2018 Nissan Leaf IC * Add fwdCamera FW for Xtrail 2017 * add missing comma * Add 0x8 default offset needed e.g. for xtrail engine ecu * Apply suggestions from code review Co-authored-by: Willem Melching <willem.melching@gmail.com> * Fix variable naming * remove unnecessary part of standard fw query * add fw versions for CAN-GW ECU * transmission eco does not reliable answer -> remove * add Altima FWs (part 1) * add Altima EPS FW * Remove NISSAN_KWP_DIAGNOSTIC_REQUEST and cleanup * Revert "Remove NISSAN_KWP_DIAGNOSTIC_REQUEST and cleanup" This reverts commit 808d6f1562e9a6cb57ea365d184b74d51f6da017. * cleanup Co-authored-by: Martin Lillepuu <martin@mlp.ee> Co-authored-by: Julian Pieles <julian@pieles.digital> Co-authored-by: Willem Melching <willem.melching@gmail.com>
4 years ago
"nissan",
[NISSAN_DIAGNOSTIC_REQUEST_KWP, NISSAN_VERSION_REQUEST_KWP],
[NISSAN_DIAGNOSTIC_RESPONSE_KWP, NISSAN_VERSION_RESPONSE_KWP],
rx_offset=NISSAN_RX_OFFSET,
Fingerprinting v2 support for Nissan vehicles (#22655) * Add Nissan FPv2 * Update Nissan version response * Add second Nissan FPv2 query * Add multiple offsets for Nissan * Nissan offset fix * Add Nissan diagnostic request * Use 21 83 for Nissan version request * Add partial 2018 Leaf FPv2 * Update 2018 Leaf FPv2 * Nissan FPv2: more RX offsets, add no diagostic request scan for 0x20 * Add FPv2 imports to Nissan values * Change LEAF to LEAF_IC It is an instrument cluster leaf. With this minor change it works :). * fixed ecu addr for nissan eps * Remove FW not used for fpv2 * Remove unused offsets * add fw versions for 2018 Nissan Leaf JP * add standard ecu fw query * add esp fw * add nissan xtrail * Add missing ESP FW for 2018 Nissan Leaf IC * Add fwdCamera FW for Xtrail 2017 * add missing comma * Add 0x8 default offset needed e.g. for xtrail engine ecu * Apply suggestions from code review Co-authored-by: Willem Melching <willem.melching@gmail.com> * Fix variable naming * remove unnecessary part of standard fw query * add fw versions for CAN-GW ECU * transmission eco does not reliable answer -> remove * add Altima FWs (part 1) * add Altima EPS FW * Remove NISSAN_KWP_DIAGNOSTIC_REQUEST and cleanup * Revert "Remove NISSAN_KWP_DIAGNOSTIC_REQUEST and cleanup" This reverts commit 808d6f1562e9a6cb57ea365d184b74d51f6da017. * cleanup Co-authored-by: Martin Lillepuu <martin@mlp.ee> Co-authored-by: Julian Pieles <julian@pieles.digital> Co-authored-by: Willem Melching <willem.melching@gmail.com>
4 years ago
),
Request(
Fingerprinting v2 support for Nissan vehicles (#22655) * Add Nissan FPv2 * Update Nissan version response * Add second Nissan FPv2 query * Add multiple offsets for Nissan * Nissan offset fix * Add Nissan diagnostic request * Use 21 83 for Nissan version request * Add partial 2018 Leaf FPv2 * Update 2018 Leaf FPv2 * Nissan FPv2: more RX offsets, add no diagostic request scan for 0x20 * Add FPv2 imports to Nissan values * Change LEAF to LEAF_IC It is an instrument cluster leaf. With this minor change it works :). * fixed ecu addr for nissan eps * Remove FW not used for fpv2 * Remove unused offsets * add fw versions for 2018 Nissan Leaf JP * add standard ecu fw query * add esp fw * add nissan xtrail * Add missing ESP FW for 2018 Nissan Leaf IC * Add fwdCamera FW for Xtrail 2017 * add missing comma * Add 0x8 default offset needed e.g. for xtrail engine ecu * Apply suggestions from code review Co-authored-by: Willem Melching <willem.melching@gmail.com> * Fix variable naming * remove unnecessary part of standard fw query * add fw versions for CAN-GW ECU * transmission eco does not reliable answer -> remove * add Altima FWs (part 1) * add Altima EPS FW * Remove NISSAN_KWP_DIAGNOSTIC_REQUEST and cleanup * Revert "Remove NISSAN_KWP_DIAGNOSTIC_REQUEST and cleanup" This reverts commit 808d6f1562e9a6cb57ea365d184b74d51f6da017. * cleanup Co-authored-by: Martin Lillepuu <martin@mlp.ee> Co-authored-by: Julian Pieles <julian@pieles.digital> Co-authored-by: Willem Melching <willem.melching@gmail.com>
4 years ago
"nissan",
[NISSAN_VERSION_REQUEST_STANDARD],
[NISSAN_VERSION_RESPONSE_STANDARD],
rx_offset=NISSAN_RX_OFFSET,
Fingerprinting v2 support for Nissan vehicles (#22655) * Add Nissan FPv2 * Update Nissan version response * Add second Nissan FPv2 query * Add multiple offsets for Nissan * Nissan offset fix * Add Nissan diagnostic request * Use 21 83 for Nissan version request * Add partial 2018 Leaf FPv2 * Update 2018 Leaf FPv2 * Nissan FPv2: more RX offsets, add no diagostic request scan for 0x20 * Add FPv2 imports to Nissan values * Change LEAF to LEAF_IC It is an instrument cluster leaf. With this minor change it works :). * fixed ecu addr for nissan eps * Remove FW not used for fpv2 * Remove unused offsets * add fw versions for 2018 Nissan Leaf JP * add standard ecu fw query * add esp fw * add nissan xtrail * Add missing ESP FW for 2018 Nissan Leaf IC * Add fwdCamera FW for Xtrail 2017 * add missing comma * Add 0x8 default offset needed e.g. for xtrail engine ecu * Apply suggestions from code review Co-authored-by: Willem Melching <willem.melching@gmail.com> * Fix variable naming * remove unnecessary part of standard fw query * add fw versions for CAN-GW ECU * transmission eco does not reliable answer -> remove * add Altima FWs (part 1) * add Altima EPS FW * Remove NISSAN_KWP_DIAGNOSTIC_REQUEST and cleanup * Revert "Remove NISSAN_KWP_DIAGNOSTIC_REQUEST and cleanup" This reverts commit 808d6f1562e9a6cb57ea365d184b74d51f6da017. * cleanup Co-authored-by: Martin Lillepuu <martin@mlp.ee> Co-authored-by: Julian Pieles <julian@pieles.digital> Co-authored-by: Willem Melching <willem.melching@gmail.com>
4 years ago
),
# Body
Request(
"body",
[TESTER_PRESENT_REQUEST, UDS_VERSION_REQUEST],
[TESTER_PRESENT_RESPONSE, UDS_VERSION_RESPONSE],
bus=0,
),
# Chrysler / FCA / Stellantis
Request(
"chrysler",
[CHRYSLER_VERSION_REQUEST],
[CHRYSLER_VERSION_RESPONSE],
whitelist_ecus=[Ecu.esp, Ecu.eps, Ecu.srs, Ecu.gateway, Ecu.fwdRadar, Ecu.fwdCamera, Ecu.combinationMeter],
rx_offset=CHRYSLER_RX_OFFSET,
),
Request(
"chrysler",
[CHRYSLER_VERSION_REQUEST],
[CHRYSLER_VERSION_RESPONSE],
whitelist_ecus=[Ecu.engine, Ecu.transmission],
),
# Ford
Request(
"ford",
[TESTER_PRESENT_REQUEST, FORD_VERSION_REQUEST],
[TESTER_PRESENT_RESPONSE, FORD_VERSION_RESPONSE],
whitelist_ecus=[Ecu.engine],
),
Request(
"ford",
[TESTER_PRESENT_REQUEST, FORD_VERSION_REQUEST],
[TESTER_PRESENT_RESPONSE, FORD_VERSION_RESPONSE],
bus=0,
whitelist_ecus=[Ecu.eps, Ecu.esp, Ecu.fwdRadar, Ecu.fwdCamera],
),
5 years ago
]
5 years ago
def chunks(l, n=128):
for i in range(0, len(l), n):
yield l[i:i + n]
def build_fw_dict(fw_versions, filter_brand=None):
fw_versions_dict = defaultdict(set)
for fw in fw_versions:
if filter_brand is None or fw.brand == filter_brand:
addr = fw.address
sub_addr = fw.subAddress if fw.subAddress != 0 else None
fw_versions_dict[(addr, sub_addr)].add(fw.fwVersion)
return dict(fw_versions_dict)
def get_brand_addrs():
versions = get_interface_attr('FW_VERSIONS', ignore_none=True)
brand_addrs = defaultdict(set)
for brand, cars in versions.items():
for fw in cars.values():
brand_addrs[brand] |= {(addr, sub_addr) for _, addr, sub_addr in fw.keys()}
return brand_addrs
def match_fw_to_car_fuzzy(fw_versions_dict, log=True, exclude=None):
"""Do a fuzzy FW match. This function will return a match, and the number of firmware version
that were matched uniquely to that specific car. If multiple ECUs uniquely match to different cars
the match is rejected."""
# These ECUs are known to be shared between models (EPS only between hybrid/ICE version)
# Getting this exactly right isn't crucial, but excluding camera and radar makes it almost
# impossible to get 3 matching versions, even if two models with shared parts are released at the same
# time and only one is in our database.
exclude_types = [Ecu.fwdCamera, Ecu.fwdRadar, Ecu.eps, Ecu.debug]
# Build lookup table from (addr, sub_addr, fw) to list of candidate cars
all_fw_versions = defaultdict(list)
for candidate, fw_by_addr in FW_VERSIONS.items():
if candidate == exclude:
continue
for addr, fws in fw_by_addr.items():
if addr[0] in exclude_types:
continue
for f in fws:
all_fw_versions[(addr[1], addr[2], f)].append(candidate)
match_count = 0
candidate = None
for addr, versions in fw_versions_dict.items():
for version in versions:
# All cars that have this FW response on the specified address
candidates = all_fw_versions[(addr[0], addr[1], version)]
if len(candidates) == 1:
match_count += 1
if candidate is None:
candidate = candidates[0]
# We uniquely matched two different cars. No fuzzy match possible
elif candidate != candidates[0]:
return set()
if match_count >= 2:
if log:
cloudlog.error(f"Fingerprinted {candidate} using fuzzy match. {match_count} matching ECUs")
return {candidate}
else:
return set()
def match_fw_to_car_exact(fw_versions_dict):
"""Do an exact FW match. Returns all cars that match the given
FW versions for a list of "essential" ECUs. If an ECU is not considered
essential the FW version can be missing to get a fingerprint, but if it's present it
needs to match the database."""
invalid = []
candidates = FW_VERSIONS
5 years ago
for candidate, fws in candidates.items():
for ecu, expected_versions in fws.items():
ecu_type = ecu[0]
addr = ecu[1:]
found_versions = fw_versions_dict.get(addr, set())
if ecu_type == Ecu.esp and candidate in (TOYOTA.RAV4, TOYOTA.COROLLA, TOYOTA.HIGHLANDER, TOYOTA.SIENNA, TOYOTA.LEXUS_IS) and not len(found_versions):
continue
# On some Toyota models, the engine can show on two different addresses
if ecu_type == Ecu.engine and candidate in (TOYOTA.CAMRY, TOYOTA.COROLLA_TSS2, TOYOTA.CHR, TOYOTA.LEXUS_IS) and not len(found_versions):
continue
# Ignore non essential ecus
if ecu_type not in ESSENTIAL_ECUS and not len(found_versions):
5 years ago
continue
# Virtual debug ecu doesn't need to match the database
if ecu_type == Ecu.debug:
continue
if not any([found_version in expected_versions for found_version in found_versions]):
5 years ago
invalid.append(candidate)
break
return set(candidates.keys()) - set(invalid)
def match_fw_to_car(fw_versions, allow_exact=True, allow_fuzzy=True):
# Try exact matching first
exact_matches = []
if allow_exact:
exact_matches = [(True, match_fw_to_car_exact)]
if allow_fuzzy:
exact_matches.append((False, match_fw_to_car_fuzzy))
brands = get_interface_attr('FW_VERSIONS', ignore_none=True).keys()
for exact_match, match_func in exact_matches:
# For each brand, attempt to fingerprint using all FW returned from its queries
matches = set()
for brand in brands:
fw_versions_dict = build_fw_dict(fw_versions, filter_brand=brand)
matches |= match_func(fw_versions_dict)
if len(matches):
return exact_match, matches
return True, set()
def get_present_ecus(logcan, sendcan):
queries = list()
parallel_queries = list()
responses = set()
versions = get_interface_attr('FW_VERSIONS', ignore_none=True)
for r in REQUESTS:
if r.brand not in versions:
continue
for brand_versions in versions[r.brand].values():
for ecu_type, addr, sub_addr in brand_versions:
# Only query ecus in whitelist if whitelist is not empty
if len(r.whitelist_ecus) == 0 or ecu_type in r.whitelist_ecus:
a = (addr, sub_addr, r.bus)
# Build set of queries
if sub_addr is None:
if a not in parallel_queries:
parallel_queries.append(a)
else: # subaddresses must be queried one by one
if [a] not in queries:
queries.append([a])
# Build set of expected responses to filter
response_addr = uds.get_rx_addr_for_tx_addr(addr, r.rx_offset)
responses.add((response_addr, sub_addr, r.bus))
queries.insert(0, parallel_queries)
ecu_responses: Set[Tuple[int, Optional[int], int]] = set()
for query in queries:
ecu_responses.update(get_ecu_addrs(logcan, sendcan, set(query), responses, timeout=0.1))
return ecu_responses
def get_brand_ecu_matches(ecu_rx_addrs):
"""Returns dictionary of brands and matches with ECUs in their FW versions"""
5 years ago
brand_addrs = get_brand_addrs()
brand_matches = {r.brand: set() for r in REQUESTS}
brand_rx_offsets = set((r.brand, r.rx_offset) for r in REQUESTS)
for addr, sub_addr, _ in ecu_rx_addrs:
# Since we can't know what request an ecu responded to, add matches for all possible rx offsets
for brand, rx_offset in brand_rx_offsets:
a = (uds.get_rx_addr_for_tx_addr(addr, -rx_offset), sub_addr)
if a in brand_addrs[brand]:
brand_matches[brand].add(a)
return brand_matches
def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, debug=False, progress=False):
"""Queries for FW versions ordering brands by likelihood, breaks when exact match is found"""
5 years ago
all_car_fw = []
brand_matches = get_brand_ecu_matches(ecu_rx_addrs)
for brand in sorted(brand_matches, key=lambda b: len(brand_matches[b]), reverse=True):
car_fw = get_fw_versions(logcan, sendcan, query_brand=brand, timeout=timeout, debug=debug, progress=progress)
all_car_fw.extend(car_fw)
# Try to match using FW returned from this brand only
matches = match_fw_to_car_exact(build_fw_dict(car_fw))
if len(matches) == 1:
break
return all_car_fw
def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, debug=False, progress=False):
versions = get_interface_attr('FW_VERSIONS', ignore_none=True)
if query_brand is not None:
versions = {query_brand: versions[query_brand]}
5 years ago
if extra is not None:
versions.update(extra)
# Extract ECU addresses to query from fingerprints
# ECUs using a subaddress need be queried one by one, the rest can be done in parallel
addrs = []
parallel_addrs = []
ecu_types = {}
for brand, brand_versions in versions.items():
for c in brand_versions.values():
for ecu_type, addr, sub_addr in c.keys():
a = (brand, addr, sub_addr)
if a not in ecu_types:
ecu_types[a] = ecu_type
5 years ago
if sub_addr is None:
if a not in parallel_addrs:
parallel_addrs.append(a)
else:
if [a] not in addrs:
addrs.append([a])
5 years ago
addrs.insert(0, parallel_addrs)
# Get versions and build capnp list to put into CarParams
car_fw = []
requests = [r for r in REQUESTS if query_brand is None or r.brand == query_brand]
for addr in tqdm(addrs, disable=not progress):
5 years ago
for addr_chunk in chunks(addr):
for r in requests:
5 years ago
try:
addrs = [(a, s) for (b, a, s) in addr_chunk if b in (r.brand, 'any') and
(len(r.whitelist_ecus) == 0 or ecu_types[(b, a, s)] in r.whitelist_ecus)]
if addrs:
query = IsoTpParallelQuery(sendcan, logcan, r.bus, addrs, r.request, r.response, r.rx_offset, debug=debug)
for (addr, rx_addr), version in query.get_data(timeout).items():
f = car.CarParams.CarFw.new_message()
5 years ago
f.ecu = ecu_types.get((r.brand, addr[0], addr[1]), Ecu.unknown)
f.fwVersion = version
f.address = addr[0]
f.responseAddress = rx_addr
f.request = r.request
f.brand = r.brand
f.bus = r.bus
5 years ago
if addr[1] is not None:
f.subAddress = addr[1]
5 years ago
car_fw.append(f)
except Exception:
cloudlog.warning(f"FW query exception: {traceback.format_exc()}")
5 years ago
return car_fw
5 years ago
if __name__ == "__main__":
import time
import argparse
import cereal.messaging as messaging
from selfdrive.car.vin import get_vin
parser = argparse.ArgumentParser(description='Get firmware version of ECUs')
parser.add_argument('--scan', action='store_true')
parser.add_argument('--debug', action='store_true')
parser.add_argument('--brand', help='Only query addresses/with requests for this brand')
5 years ago
args = parser.parse_args()
logcan = messaging.sub_sock('can')
sendcan = messaging.pub_sock('sendcan')
extra: Any = None
5 years ago
if args.scan:
extra = {}
5 years ago
# Honda
for i in range(256):
extra[(Ecu.unknown, 0x18da00f1 + (i << 8), None)] = []
extra[(Ecu.unknown, 0x700 + i, None)] = []
extra[(Ecu.unknown, 0x750, i)] = []
extra = {"any": {"debug": extra}}
5 years ago
time.sleep(1.)
t = time.time()
print("Getting vin...")
addr, vin_rx_addr, vin = get_vin(logcan, sendcan, 1, retry=10, debug=args.debug)
print(f'TX: {hex(addr)}, RX: {hex(vin_rx_addr)}, VIN: {vin}')
print(f"Getting VIN took {time.time() - t:.3f} s")
5 years ago
print()
t = time.time()
fw_vers = get_fw_versions(logcan, sendcan, query_brand=args.brand, extra=extra, debug=args.debug, progress=True)
_, candidates = match_fw_to_car(fw_vers)
5 years ago
print()
print("Found FW versions")
print("{")
padding = max([len(fw.brand) for fw in fw_vers] or [0])
5 years ago
for version in fw_vers:
subaddr = None if version.subAddress == 0 else hex(version.subAddress)
print(f" Brand: {version.brand:{padding}}, bus: {version.bus} - (Ecu.{version.ecu}, {hex(version.address)}, {subaddr}): [{version.fwVersion}]")
5 years ago
print("}")
print()
print("Possible matches:", candidates)
print(f"Getting fw took {time.time() - t:.3f} s")