eepromtool/eepromtool/ecu.py
2025-05-06 16:48:43 +02:00

371 Zeilen
12 KiB
Python

import logging
from .eepromtool import eeprom
class ecueeprom:
bin_type = "ECU_eeprom"
noFixCheckSum = False
noCheckSumPages = [0x00,0x11,0x12,0x13,0x14]
backupPage = [0x8,0xA,0xC,0xE,0x10,0x1F]
write = False #set to write out eeprom after modification
size = 0
data = []
immover = 3
vin = ""
vindetail = ""
immoid = ""
skc = ""
clustercode = "" #0x34-0x3A
softcoding = "" #0x7A 0x7B
immoval = []
tunertag = ""
flashattemptcount = 0
flashsuccessfulcount = 0
dtc = None
immo = None #True indicates immo is on, False is off, None is error
csum = True #True indicates checksum is ok, false indicates checksum is wrong
R32 = False #True indiciates that this is a R32 bin 1024kb bin with FF padding
def __init__(self,ndata):
logging.warn("Parsing as ECU eeprom")
self.data = ndata
self.parse()
def parse(self):
R32 = False
#try to work out of this is immo2, immo3 or immo3r32
tmpval = self.data[0x00][0x00]
for bite in range(0x01,0x05):
if self.data[0x00][bite] != tmpval:
#first 5 bytes are not the same, assume immo2 (or some new immo 3)
self.immover = 2
if self.immover == 3:
if self.data[0x00][0x00] == 0x30:
logging.info("Bin starts 30 30 30 30 30 - is this a 1024kb R32 bin?")
#R32 = True
#set length
self.size = 0
for page in self.data:
self.size += len(page)
#Parse R32 1kb bins
if self.size == 1024 or R32 == True:
logging.info("Found 1024b bin - parsing as a R32 bin - experimental")
for page in range(0x20,0x40):
for bite in self.data[page]:
if bite != 0xFF:
R32=False
if R32:
logging.info("The last 512bytes are padded with 0xFF, this appears to be an R32 bin")
self.R32 = True
#add the padding bytes to nochecksumpages
self.noCheckSumPages.append(0x1C)
self.noCheckSumPages.append(0x1D)
for page in range(0x20,0x40):
self.noCheckSumPages.append(page)
else:
logging.error("ERROR: 1024byte file detected, but the last 512bytes are not all padded with 0xFF, this file is may be a corupt dump, a cluster dump or something other than an eeprom dump, open it with a hex editor - if this is a valid eeprom please post the file and vehicle info on the thread for this tool")
#get SKC
hexskc = "%0.2x%0.2x" % (self.data[0x3][0x3],self.data[0x3][0x2])
self.skc = "0%0.4d" % int(hexskc,16)
#get death DTC
if self.data[0x1][0xC] + self.data[0x2][0xC] > 0:
self.dtc = "%0.2X %0.2X" % (self.data[0x1][0xC],self.data[0x2][0xC])
#get immo
immo1 = self.data[0x1][0x2]
immo2 = self.data[0x2][0x2]
if immo1 == immo2:
#if byte 12 and byte 22 are not set the same, leave self.immo None to indicate error
if immo1 == 0x01:
self.immo = True
elif immo1 == 0x02:
self.immo = False
#if byte 12 and 22 are not set to 0x01 or 0x02, fall through to indicate error, set immoval to the actual value for display
self.immoval.append(immo1)
self.immoval.append(immo2)
#get cluster code
self.clustercode = ""
for i in range(0x4, 0xB):
self.clustercode += "%0.2X " % self.data[0x3][i]
#get VIN
vinstring = bytearray()
for bite in range (0x05,0xA):
vinstring.append(self.data[0xB][bite])
for bite in range (0x00,0xC):
vinstring.append(self.data[0xD][bite])
try:
self.vin = vinstring.decode('ascii')
except:
logging.warn("WARNING: cannot decode VIN, try setting VIN to fix")
self.vindetail = eeprom.parsevin(self.vin)
#get softcoding
hexscing = "%0.2x%0.2x" % (self.data[0x7][0xB],self.data[0x7][0xA])
self.softcoding = "%0.4d" % int(hexscing,16)
#get Immo ID
immostring = bytearray()
immostring.append(self.data[0xD][0xC])
for bite in range (0x00,0xD):
immostring.append(self.data[0xF][bite])
try:
self.immoid = immostring.decode('ascii')
except:
logging.warn("WARNING: cannot decode ImmmoID")
#get flasher tag
try:
self.tunertag = self.data[0x1E][0x2:0x8].decode('ascii')
except:
pass
#get flash programming counts
self.flashsuccessfulcount = self.data[0x1E][0x9]
self.flashattemptcount = self.data[0x1E][0xA]
#validate checksum
self.validateChecksum()
def validateChecksum(self):
try:
logging.debug("Validating Checksum")
minus = 1
self.csum = True
#FFFF - (page number - 1) - sum(first 14 bytes)
for pageno in range(0x00,len(self.data)):
#skip pages without a checksum
if pageno in self.noCheckSumPages:
logging.debug("Skipping nochecksumpage: %0.2X" % pageno)
continue
#if this is a backup page, use the previous page's no to calculate the checksum
if pageno in self.backupPage:
minus = 2
else:
minus = 1
bytesum = 0x00
for i in range (0, 14):
bytesum += self.data[pageno][i]
calcsum = 0xFFFF - (pageno - minus) - bytesum
#get checksum for this page from the bin
savedsum = "%0.2X%0.2X" % (self.data[pageno][0x0F],self.data[pageno][0x0E])
if calcsum != int(savedsum,16):
self.csum = False
logging.debug("Checksum Error page: %0.2X" % pageno)
else:
logging.debug("Checksum OK page: %0.2X" % pageno)
except:
logging.warn("WARNING: Cannot validate checksum, possible invalid input")
self.csum = False
if self.csum == True:
logging.debug("Checksum OK")
else:
logging.debug("Checksum error or validation failure")
def fixChecksum(self):
"""
Fixes the checksum and sets the backup rows
"""
#if the nfc flag is set (from the command line), return without doing checksum/backups
if self.noFixCheckSum:
logging.info("- Skipping page backup and checksum correction")
return
logging.info("- Setting backup pages")
#do backups
for pageno in range(0x00,len(self.data)):
if pageno in self.backupPage:
self.data[pageno] = self.data[pageno - 1]
logging.info("- Correcting checksums")
#do checksums
minus = 1
self.csum = True
#FFFF - (page number - 1) - sum(first 14 bytes)
for pageno in range(0x00,len(self.data)):
#skip pages without a checksum
if pageno in self.noCheckSumPages:
continue
#if this is a backup page, use the previous page's no to calculate the checksum
if pageno in self.backupPage:
minus = 2
else:
minus = 1
bytesum = 0x00
for i in range (0, 14):
bytesum += self.data[pageno][i]
calcsum = 0xFFFF - (pageno - minus) - bytesum
self.data[pageno][0x0F] = (calcsum / 256)
self.data[pageno][0x0E] = (calcsum % 256)
def getChecksum(self):
if self.csum:
return "OK"
else:
return "Invalid Checksum"
def writebin(self,outputfile):
try:
self.fixChecksum()
except:
logging.warn("Warning: Error correcting checksum, checksums in output file may be invalid")
outputdata = bytearray()
for row in self.data:
for bite in row:
outputdata.append(bite)
print("")
logging.debug("Writing %dkb ECU eeprom bin to: %s" % (len(outputdata),outputfile))
try:
with open(outputfile,'wb') as fp:
fp.write(outputdata)
except:
logging.error("Error: Could not open output file, exiting")
exit(1)
logging.info("- Write Successful")
def printStatus(self):
logging.info("EEPROM Status:")
logging.info("- Type: %s" % self.bin_type)
logging.info("- Version: Immo%d" % self.immover)
if self.getVINDetail() != "":
logging.info("- VIN: %s (%s)" % (self.getVIN(),self.getVINDetail()))
else:
logging.info("- VIN: %s" % (self.getVIN()))
logging.info("- SKC: " + self.getSKC())
logging.info("- Immobiliser: " + self.getImmo())
logging.info("- Checksum: " + self.getChecksum())
logging.info("- Size: " + self.getLength())
logging.info("- Cluster Code: " + self.getClusterCode())
logging.info("- P0601 DTC: " + self.getDTC())
logging.info("- Immo ID: " + self.getImmoID())
logging.info("- Softcoding ID: " + self.getSoftcoding())
logging.info("- Tuner Tag: " + self.getTunerTag())
logging.info("- Flash Programming (successful): %d" % self.flashsuccessfulcount)
logging.info("- Flash Programming (attempts): %d" % self.flashattemptcount)
logging.info("")
def getLength(self):
if self.size == 512:
return "%dbytes" % self.size
elif self.size/1024 == 512:
logging.warn("Size: 512kb - is this a flash bin not an eeprom bin?")
logging.warn("Exiting")
exit(1)
elif self.size/1024 == 1024:
logging.warn("Size: 1024kb - is this a flash bin not an eeprom bin?")
logging.warn("Exiting")
exit(1)
else:
return "%dbytes - 512bytes expected, check this is a eeprom bin" % self.size
def getTunerTag(self):
if self.tunertag == "":
return "Not Set"
else:
return self.tunertag
def getSoftcoding(self):
return self.softcoding
def getSKC(self):
return self.skc
def getClusterCode(self):
return self.clustercode
def getDTC(self):
if self.dtc is not None:
return "set to " + self.dtc
else:
return "not set"
def clearDTC(self):
self.data[0x1c] = 0x00
self.data[0x2c] = 0x00
self.parse()
self.write = True
def getImmo(self):
"""
Get the current setting for the immobiliser, returns text string to indicate status
"""
if self.immo is True:
return "On"
elif self.immo is False:
return "Off"
else:
return "Error, set Immo - Current values: 0x12 = 0x%0.2X, 0x22 = 0x%0.2X" % (self.immoval[0],self.immoval[1])
def setImmo(self,setting):
"""
Set the immo on or off
"""
if setting is True:
logging.info("Setting Immobiliser: On")
if self.immo is True:
logging.info("- Immobiliser already set On")
return
#Set immobiliser on
self.data[0x1][0x2] = 0x01
self.data[0x2][0x2] = 0x01
logging.info("- Immobiliser On")
else:
logging.info("Setting Immobiliser: Off")
if self.immo is False:
logging.info("- Immobiliser already set Off")
return
#Set immo off
self.data[0x1][0x2] = 0x02
self.data[0x2][0x2] = 0x02
logging.info("- Immobiliser Off")
self.parse()
self.write = True
def getVIN(self):
return self.vin
def getVINDetail(self):
return self.vindetail
def setVIN(self,newvin):
if len(newvin) != 17:
logging.error("ERROR: VIN number must be 17 characters")
exit(1)
logging.info("Setting VIN to %s" % newvin)
bavin = bytearray(newvin,'ascii')
self.data[0xB][0x05:0xA] = bavin[0x00:0x05]
del bavin[0:5]
self.data[0xD][0x00:0xC] = bavin
try:
a = 1
except:
logging.error("ERROR: Could not set VIN")
exit(1)
self.parse()
self.write = True
def getImmoID(self):
return self.immoid