@ -228,13 +228,13 @@ def chunks(l, n=128):
def build_fw_dict ( fw_versions , filter_brand = None ) :
def build_fw_dict ( fw_versions , filter_brand = None ) :
fw_versions_dict = { }
fw_versions_dict = defaultdict ( set )
for fw in fw_versions :
for fw in fw_versions :
if filter_brand is None or fw . brand == filter_brand :
if filter_brand is None or fw . brand == filter_brand :
addr = fw . address
addr = fw . address
sub_addr = fw . subAddress if fw . subAddress != 0 else None
sub_addr = fw . subAddress if fw . subAddress != 0 else None
fw_versions_dict [ ( addr , sub_addr ) ] = fw . fwVersion
fw_versions_dict [ ( addr , sub_addr ) ] . add ( fw . fwVersion )
return fw_versions_dict
return dict ( fw_versions_dict )
def get_brand_addrs ( ) :
def get_brand_addrs ( ) :
@ -271,7 +271,8 @@ def match_fw_to_car_fuzzy(fw_versions_dict, log=True, exclude=None):
match_count = 0
match_count = 0
candidate = None
candidate = None
for addr , version in fw_versions_dict . items ( ) :
for addr , versions in fw_versions_dict . items ( ) :
for version in versions :
# All cars that have this FW response on the specified address
# All cars that have this FW response on the specified address
candidates = all_fw_versions [ ( addr [ 0 ] , addr [ 1 ] , version ) ]
candidates = all_fw_versions [ ( addr [ 0 ] , addr [ 1 ] , version ) ]
@ -303,23 +304,23 @@ def match_fw_to_car_exact(fw_versions_dict):
for ecu , expected_versions in fws . items ( ) :
for ecu , expected_versions in fws . items ( ) :
ecu_type = ecu [ 0 ]
ecu_type = ecu [ 0 ]
addr = ecu [ 1 : ]
addr = ecu [ 1 : ]
found_version = fw_versions_dict . get ( addr , None )
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 found_version is None :
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
continue
# On some Toyota models, the engine can show on two different addresses
# 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 found_version is None :
if ecu_type == Ecu . engine and candidate in ( TOYOTA . CAMRY , TOYOTA . COROLLA_TSS2 , TOYOTA . CHR , TOYOTA . LEXUS_IS ) and not len ( found_versions ) :
continue
continue
# Ignore non essential ecus
# Ignore non essential ecus
if ecu_type not in ESSENTIAL_ECUS and found_version is None :
if ecu_type not in ESSENTIAL_ECUS and not len ( found_versions ) :
continue
continue
# Virtual debug ecu doesn't need to match the database
# Virtual debug ecu doesn't need to match the database
if ecu_type == Ecu . debug :
if ecu_type == Ecu . debug :
continue
continue
if found_version not in expected_versions :
if not any ( [ found_version in expected_versions for found_version in found_versions ] ) :
invalid . append ( candidate )
invalid . append ( candidate )
break
break
@ -327,18 +328,15 @@ def match_fw_to_car_exact(fw_versions_dict):
def match_fw_to_car ( fw_versions , allow_fuzzy = True ) :
def match_fw_to_car ( fw_versions , allow_fuzzy = True ) :
versions = get_interface_attr ( ' FW_VERSIONS ' , ignore_none = True )
# Try exact matching first
# Try exact matching first
exact_matches = [ ( True , match_fw_to_car_exact ) ]
exact_matches = [ ( True , match_fw_to_car_exact ) ]
if allow_fuzzy :
if allow_fuzzy :
exact_matches . append ( ( False , match_fw_to_car_fuzzy ) )
exact_matches . append ( ( False , match_fw_to_car_fuzzy ) )
for exact_match , match_func in exact_matches :
for exact_match , match_func in exact_matches :
# For each brand, attempt to fingerprint using FW returned from its queries
# TODO: For each brand, attempt to fingerprint using only FW returned from its queries
matches = set ( )
matches = set ( )
for brand in versions . keys ( ) :
fw_versions_dict = build_fw_dict ( fw_versions , filter_brand = None )
fw_versions_dict = build_fw_dict ( fw_versions , filter_brand = brand )
matches | = match_func ( fw_versions_dict )
matches | = match_func ( fw_versions_dict )
if len ( matches ) :
if len ( matches ) :
@ -408,7 +406,9 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, debug=Fa
for brand in sorted ( brand_matches , key = lambda b : len ( brand_matches [ b ] ) , reverse = True ) :
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 )
car_fw = get_fw_versions ( logcan , sendcan , query_brand = brand , timeout = timeout , debug = debug , progress = progress )
all_car_fw . extend ( car_fw )
all_car_fw . extend ( car_fw )
matches = match_fw_to_car_exact ( build_fw_dict ( car_fw ) )
# TODO: Until erroneous FW versions are removed, try to fingerprint on all possible combinations so far
_ , matches = match_fw_to_car ( all_car_fw , allow_fuzzy = False )
if len ( matches ) == 1 :
if len ( matches ) == 1 :
break
break