class LoopbackTester(object): def __init__(self): self.device = Device() def test_loopback(self, lengths): self.device.flush() time.sleep(0.1) for l in lengths: test_str = test_string(l) if self.device.write(test_str) != len(test_str): sys.stdout.write('*') time.sleep(0.1) result = '' for _ in range(3): result = self.device.read(l) if result: break if result != test_str: self.device.flush() time.sleep(0.25) yield result == test_str def main(self): for bd in [9600, 31250, 115200, 1152000]: self.device.baudrate = bd for result in self.test_loopback(range(1, 50) + range(100, 500, 100) + range(1000, 5000, 1000)): if result: sys.stdout.write('+') else: sys.stdout.write('!') sys.stdout.write('\n')
class LoopbackTester(object): def __init__(self): self.device = Device() def test_loopback(self, lengths): self.device.flush() time.sleep(0.1) for l in lengths: test_str = test_string(l) if self.device.write(test_str) != len(test_str): sys.stdout.write('*') time.sleep(0.1) result = '' for _ in range(3): result = self.device.read(l) if result: break if result != test_str: self.device.flush() time.sleep(0.25) yield result == test_str def main(self): for bd in [9600, 31250, 115200, 1152000]: self.device.baudrate = bd for result in self.test_loopback( range(1, 50) + range(100, 500, 100) + range(1000, 5000, 1000)): if result: sys.stdout.write('+') else: sys.stdout.write('!') sys.stdout.write('\n')
class CommunicationThread(threading.Thread): """Thread for fast serial communication""" BAUD_RATE = 2000000 # baud rate for serial communication BUFFER_SIZE = 64 # read buffer size in bytes def __init__(self, queue): """Initialize the communication worker""" # call constructor of super class threading.Thread.__init__(self) # set queue self.queue = queue # set FTDI device for communication with iCEstick try: self.dev = Device(mode='b', interface_select=INTERFACE_B) # set baudrate self.dev.baudrate = self.BAUD_RATE except: global exit_flag print(fg.li_red + "[-] Could not connect to FTDI serial interface" + fg.rs) exit(1) def run(self): """Receive data""" global exit_flag while not exit_flag: if not self.queue.full(): item = self.dev.read(self.BUFFER_SIZE) if item != b'': self.queue.put(item)
class FtdiSyncInterface: ################################################################## # Construction ################################################################## def __init__(self, iface=None): self.interface = iface self.target = None self.prog_cb = None self.CMD_NOP = 0x0 self.CMD_WR = 0x1 self.CMD_RD = 0x2 self.CMD_GP_WR = 0x3 self.CMD_GP_RD = 0x4 self.HDR_SIZE = 6 self.MAX_SIZE = 255 self.BLOCK_SIZE_WR = 64 # Really 2048 self.BLOCK_SIZE_RD = 64 self.MAGIC_ADDR = 0xF0000000 # User specified (device_id) self.dev_id = None if iface != None and iface != "": self.dev_id = iface ################################################################## # set_progress_cb: Set progress callback ################################################################## def set_progress_cb(self, prog_cb): self.prog_cb = prog_cb ################################################################## # connect: Open serial connection ################################################################## def connect(self): self.target = Device(device_id=self.dev_id, interface_select=1) self.target.flush() time.sleep(0.01) BITMODE_SYNCFF = 0x40 SIO_RTS_CTS_HS = (0x1 << 8) self.target.ftdi_fn.ftdi_set_bitmode(0, BITMODE_SYNCFF) self.target.ftdi_fn.ftdi_setflowctrl(SIO_RTS_CTS_HS) self.target.flush() ################################################################## # write: Write a block of data to a specified address ################################################################## def write(self, addr, data, length, addr_incr=True, max_block_size=-1): # Connect if required if self.target == None: self.connect() # Write blocks idx = 0 remainder = length if self.prog_cb != None: self.prog_cb(0, length) if max_block_size == -1: max_block_size = self.BLOCK_SIZE_WR while remainder > 0: l = max_block_size if l > remainder: l = remainder cmd = bytearray(2 + 4 + l) cmd[0] = (((l >> 8) & 0xF) << 4) | self.CMD_WR cmd[1] = l & 0xFF cmd[2] = (addr >> 24) & 0xFF cmd[3] = (addr >> 16) & 0xFF cmd[4] = (addr >> 8) & 0xFF cmd[5] = (addr >> 0) & 0xFF for i in range(l): cmd[6 + i] = data[idx] idx += 1 # Write to interface self.target.write(cmd) # Update display if self.prog_cb != None: self.prog_cb(idx, length) if addr_incr: addr += l remainder -= l ################################################################## # read: Read a block of data from a specified address ################################################################## def read(self, addr, length, addr_incr=True, max_block_size=-1): # Connect if required if self.target == None: self.connect() idx = 0 remainder = length data = bytearray(length) if self.prog_cb != None: self.prog_cb(0, length) if max_block_size == -1: max_block_size = self.BLOCK_SIZE_RD while remainder > 0: l = max_block_size if l > remainder: l = remainder cmd = bytearray(2 + 4) cmd[0] = (((l >> 8) & 0xF) << 4) | self.CMD_RD cmd[1] = l & 0xFF cmd[2] = (addr >> 24) & 0xFF cmd[3] = (addr >> 16) & 0xFF cmd[4] = (addr >> 8) & 0xFF cmd[5] = (addr >> 0) & 0xFF # Write to serial port self.target.write(cmd) # Read block response for i in range(l): x = bytearray() while len(x) == 0: x = self.target.read(1) data[idx] = ord(x) & 0xFF idx += 1 # Update display if self.prog_cb != None: self.prog_cb(idx, length) if addr_incr: addr += l remainder -= l return data ################################################################## # read32: Read a word from a specified address ################################################################## def read32(self, addr): # Connect if required if self.target == None: self.connect() # Send read command cmd = bytearray([ self.CMD_RD, 4, (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, (addr >> 0) & 0xFF ]) self.target.write(cmd) value = 0 idx = 0 while (idx < 4): b = self.target.read(1) value |= (ord(b) << (idx * 8)) idx += 1 return value ################################################################## # write32: Write a word to a specified address ################################################################## def write32(self, addr, value): # Connect if required if self.target == None: self.connect() # Send write command cmd = bytearray([ self.CMD_WR, 4, (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, (addr >> 0) & 0xFF, (value >> 0) & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF ]) self.target.write(cmd) ################################################################## # read_gpio: Read GPIO bus ################################################################## def read_gpio(self): # Connect if required if self.target == None: self.connect() # Send read command cmd = bytearray([self.CMD_GP_RD]) self.target.write(cmd) return ord(self.target.read(1)) ################################################################## # write_gpio: Write a byte to GPIO ################################################################## def write_gpio(self, value): # Connect if required if self.target == None: self.connect() # Send write command cmd = bytearray([self.CMD_GP_WR, (value >> 0) & 0xFF]) self.target.write(cmd)
class Ecu: def __init__(self): self.ser = Device(mode='b', lazy_open=True) def slowInit11(self): # Take the one-byte address to "bit bang" and bang the port self.bbser = BitBangDevice() print("beginning slow init") self.bbser.open() self.bbser.direction = 0x01 self.bbser.port = 1 time.sleep(.5) self.bbser.port = 0 time.sleep(.2) self.bbser.port = 1 time.sleep(.2) self.bbser.port = 0 time.sleep(1.4) self.bbser.port = 1 time.sleep(.2) self.bbser.close() print("slow init sent") def initialize(self, connect): self.connect = connect if self.connect == "SLOW-0x11": self.ser.close() time.sleep(.5) self.ecuconnect = False while self.ecuconnect == False: print("Attempting ECU connect: " + self.connect) # Bit bang the K-line self.slowInit11() self.ser.open() self.ser.ftdi_fn.ftdi_set_line_property(8, 1, 0) self.ser.baudrate = 10400 self.ser.flush() # Wait for ECU response to bit bang waithex = [0x55, 0xef, 0x8f, 1] print("Wating for init response") response = self.waitfor(waithex) print(f"Init response: {hexlist(response[2])}") # Wait a bit time.sleep(.026) # Send 0x70 self.send([0x70]) # 0xee means that we're talking to the ECU waithex = [0xfe, 1] print("waiting for ECU reponse") response = self.waitfor(waithex) print(f"ecu connection response: {hexlist(response[2])}") if response[0] == True: self.ecuconnect = True else: print("ECU Connect Failed. Retrying.") return print("INIT done") def waitfor(self, wf): # This was used for debugging and really is only used for the init at this point. # wf should be a list with the timeout in the last element self.wf = wf isfound = False idx = 0 foundlist = [] capturebytes = [] to = self.wf[-1] timecheck = time.time() while (time.time() <= (timecheck + to)) & (isfound == False): try: recvbyte = self.recvraw(1) if recvbyte != b"": recvdata = ord(recvbyte) capturebytes = capturebytes + [recvdata] if recvdata == self.wf[idx]: foundlist = foundlist + [recvdata] idx = idx + 1 else: foundlist = [] idx = 0 if idx == len(self.wf) - 1: isfound = True except e: print([isfound, foundlist, capturebytes]) print('error', e) break return [isfound, foundlist, capturebytes] def send(self, sendlist): self.sendlist = sendlist # Puts every byte in the sendlist out the serial port for i in self.sendlist: self.ser.write(chr(i)) def recvraw(self, bytes): self.bytes = bytes recvdata = self.ser.read(self.bytes) return recvdata def recv(self, bytes): self.bytes = bytes isread = False while isread == False: recvbyte = self.ser.read(self.bytes) if recvbyte != b"": recvdata = recvbyte isread = True return recvdata def sendcommand(self, sendlist): # Wraps raw KWP command in a length byte and a checksum byte and hands it to send() self.sendlist = sendlist csum = 0 self.sendlist = [len(self.sendlist)] + self.sendlist csum = self.checksum(self.sendlist) self.sendlist = self.sendlist + [csum] self.send(self.sendlist) print(f"sendcommand() sent: {hexlist(self.sendlist)}") cmdval = self.commandvalidate(self.sendlist) return cmdval def commandvalidate(self, command): # Every KWP command is echoed back. This clears out these bytes. self.command = command cv = True for i in range(len(self.command)): recvdata = self.recv(1) if ord(recvdata) != self.command[i]: cv = cv & False return cv def checksum(self, checklist): # Calculates the simple checksum for the KWP command bytes. self.checklist = checklist csum = 0 for i in self.checklist: csum = csum + i csum = (csum & 0xFF) % 0xFF return csum # used exclusivly for the errorhandling section def _raise(self, ex): raise ex def getresponse(self): # gets a properly formated KWP response from a command and returns the data. debugneeds = 4 numbytes = 0 # This is a hack because sometimes responses have leading 0x00's. Why? This removes them. while numbytes == 0: numbytes = ord(self.recv(1)) gr = [numbytes] if debug >= debugneeds: print("Get bytes: " + hex(numbytes)) for i in range(numbytes): recvdata = ord(self.recv(1)) if debug >= debugneeds: print("Get byte" + str(i) + ": " + hex(recvdata)) gr = gr + [recvdata] checkbyte = self.recv(1) if debug >= debugneeds: print(f"getresponse recieved: {hexlist(gr)}") if debug >= debugneeds: print("GR: " + hex(ord(checkbyte)) + "<-->" + hex(self.checksum(gr))) if (gr[1] == 0x7f): return { # returning the result so 0x78 (responsePending) can re-execute 0x10: lambda: self._raise(Exception("generalReject", gr)), 0x11: lambda: self._raise(Exception("busyRepeatRequest", gr)), 0x12: lambda: self._raise( Exception("subFunctionNotSupported / invalidFormat", gr)), 0x21: lambda: self._raise(Exception("busyRepeatRequest", gr)), 0x22: lambda: self._raise( Exception("conditionsNotCorrectOrRequestSequenceError", gr) ), 0x23: lambda: self._raise(Exception("routineNotComplete", gr)), 0x31: lambda: self._raise(Exception("requestOutOfRange", gr)), 0x33: lambda: self._raise( Exception("securityAccessDenied / securityAccessRequested", gr)), 0x35: lambda: self._raise(Exception("invalidKey", gr)), 0x36: lambda: self._raise(Exception("exceedNumberOfAttempts", gr)), 0x37: lambda: self._raise( Exception("requiredTimeDelayNotExpired", gr)), 0x40: lambda: self._raise(Exception("downloadNotAccepted")), 0x41: lambda: self._raise(Exception("improperDownloadType")), 0x42: lambda: self._raise( Exception("canNotDownloadToSpecifiedAddress", gr)), 0x43: lambda: self._raise( Exception("canNotDownloadNumberOfBytesRequested", gr)), 0x50: lambda: self._raise(Exception("uploadNotAccepted", gr)), 0x51: lambda: self._raise(Exception("improperUploadType", gr)), 0x52: lambda: self._raise( Exception("canNotUploadFromSpecifiedAddress", gr)), 0x53: lambda: self._raise( Exception("canNotUploadNumberOfBytesRequested", gr)), 0x71: lambda: self._raise(Exception("transferSuspended", gr)), 0x72: lambda: self._raise(Exception("transferAborted", gr)), 0x74: lambda: self._raise( Exception("illegalAddressInBlockTransfer", gr)), 0x75: lambda: self._raise( Exception("illegalByteCountInBlockTransfer", gr)), 0x76: lambda: self._raise(Exception("illegalBlockTransferType", gr)), 0x77: lambda: self._raise( Exception("blockTransferDataChecksumError", gr)), 0x78: self.getresponse, 0x79: lambda: self._raise( Exception("incorrectByteCountDuringBlockTransfer", gr)), 0x80: lambda: self._raise( Exception("serviceNotSupportedInActiveDiagnosticMode", gr) ), 0x90: lambda: self._raise(Exception("noProgramm", gr)), 0x91: lambda: self._raise( Exception("requiredTimeDelayNotExpired", gr)) }.get( gr[3], lambda: self._raise( Exception("Generic KWP negative response", gr)))() return gr def readecuid(self, paramdef): #KWP2000 command to pull the ECU ID self.paramdef = paramdef debugneeds = 3 response = self.sendcommand([0x10, 0x85]) # setup diag session reqserviceid = [0x1A] sendlist = reqserviceid + self.paramdef if debug >= debugneeds: print(f"readecuid sending: {hexlist(sendlist)}") self.sendcommand(sendlist) response = self.getresponse() if debug >= debugneeds: print(f"readecuid got: {hexlist(response)}") return response def securityAccessL3(self): ### Begin - level 3 security self.sendcommand([0x27, 0x03]) response = self.getresponse() print(f"Seed: {hexlist(response)}") ## highBytes(uint8_t) = value(uint16_t) >> 8 ## lowBytes(uint8_t) = value(uint16_t) & 0xff ## value(uint16_t) = (high << 8) + low seed = (response[3] << 24) + (response[4] << 16) + ( response[5] << 8) + response[6] print(f"Seed: {seed}") key = seed + 12233 # standard VW access keyHex = [ key >> 24 & 0xff, key >> 16 & 0xff, key >> 8 & 0xff, key & 0xff ] print(f"Seed: {hexlist(keyHex)}") self.sendcommand([0x27, 0x04] + keyHex) try: response = self.getresponse() #sometimes this doesn't work except: response = self.getresponse() print(hexlist(response)) if (response[3] != 0x34): raise Exception("failed to get L3 auth") print("End security level 3 access") # def securityAccessL1(self): # pass # ### Begin - Level 1 key/seed # ## request seed 27 01 # self.sendcommand([0x27,0x01]) # response = self.getresponse() # print(hexlist(response)) # # len? secreq level seed h seed l checksum # # 0x06 0x67 0x01 0x6D 0x20 0xFC 0xB1 0xA8 # seedH = (response[3] << 8) + response[4] # seedL = (response[5] << 8) + response[6] # magicValue = 0x1c60020 # for count in range(5): # tempstring = seedH &0x8000 # seedH = seedH << 1 # if(tempstring&0xFFFF == 0): # temp2 = seedL&0xFFFF # temp3 = tempstring&0xFFFF0000 # tempstring = temp2+temp3 # seedH = seedH&0xFFFE # temp2 = tempstring&0xFFFF # temp2 = temp2 >> 0x0F # tempstring = tempstring&0xFFFF0000 # tempstring = tempstring+temp2 # seedH = seedH|tempstring # seedL = seedL << 1 # else: # tempstring = seedL+seedL # seedH = seedH & 0xFFFE # temp2 = tempstring & 0xFF #Same as EDC15 until this point # temp3 = magicValue & 0xFFFFFF00 # temp2 = temp2 | 1 # magicValue = temp2 + temp3 # magicValue = magicValue & 0xFFFF00FF # magicValue = magicValue | tempstring # temp2 = seedL & 0xFFFF # temp3 = tempstring & 0xFFFF0000 # temp2 = temp2 >> 0x0F # tempstring = temp2 + temp3 # tempstring = tempstring | seedH # magicValue = magicValue ^ 0x1289 # tempstring = tempstring ^ 0x0A22 # seedL = magicValue # seedH = tempstring # print(f"H:{seedH} L:{seedL}") # ## high bytes = value >> 8 # ## low bytes = value & 0xff # ## value = (high<<8) + low # self.sendcommand([0x27,0x02, seedH & 0xff, seedH >>8, seedL & 0xff, seedL >> 8]) # # self.sendcommand([0x27,0x02, 0xff, 0xff, 0xff, 0xff]) # response = self.getresponse() def securityAccessL1(self): self.sendcommand([0x27, 0x01]) response = self.getresponse() print(hexlist(response)) seed = (response[3] << 24) + (response[4] << 16) + ( response[5] << 8) + response[6] print(f"L1 seed: {seed}") ''' for (byte i = 0; i < 5; i++) { if ((seed & 0x80000000) == 0x80000000) { // Check carry seed = (SEED_DATA[ecuID]) ^ ((seed << 1) | (seed >>> 31)); // rotate left and xor } else { seed = ((seed << 1) | (seed >>> 31)); // rotate left only } } return seed; ''' magicValue = 0x1c60020 def rshift(val, n): return (val >> n) & (0x7fffffff >> (n - 1)) for x in range(5): if ((seed & 0x80000000) == 0x80000000): seed = magicValue ^ ((seed << 1) | rshift(seed, 31)) else: seed = ((seed << 1) | rshift(seed, 31)) print(f"L1 key: {seed}") keyHex = [ seed >> 24 & 0xff, seed >> 16 & 0xff, seed >> 8 & 0xff, seed & 0xff ] self.sendcommand([0x27, 0x02] + keyHex) # self.sendcommand([0x27,0x02, 0xff, 0xff, 0xff, 0xff]) return self.getresponse() def stopcomm(self): # KWP2000 command to tell the ECU that the communications is finished stopcommunication = [0x82] self.sendcommand(stopcommunication) response = self.getresponse() return response def startdiagsession(self, bps): # KWP2000 setup that sets the baud for the logging session self.bps = bps startdiagnosticsession = [0x10] sessionType = [0x86] # if self.bps == 10400: # bpsout = [ 0x?? ] # if self.bps == 14400: # bpsout = [ 0x?? ] if self.bps == 19200: bpsout = [0x30] if self.bps == 38400: bpsout = [0x50] if self.bps == 56000: bpsout = [0x63] if self.bps == 57600: bpsout = [0x64] if self.bps == 124800: bpsout = [0x87] if self.bps == 250000: bpsout = [0xA7] if (self.bps != 0): sendlist = startdiagnosticsession + sessionType + bpsout else: sendlist = startdiagnosticsession + sessionType self.sendcommand(sendlist) response = self.getresponse() self.ser.baudrate = self.bps time.sleep(1) return response def accesstimingparameter(self, params): # KWP2000 command to access timing parameters self.params = params accesstiming_setval = [0x83, 0x03] accesstiming = accesstiming_setval + self.params sendlist = accesstiming self.sendcommand(sendlist) response = self.getresponse() return response def readmembyaddr(self, readvals): # Function to read an area of ECU memory. debugneeds = 4 self.readvals = readvals rdmembyaddr = [0x23] sendlist = rdmembyaddr + self.readvals if debug >= debugneeds: print("readmembyaddr() sendlist: " + hexlist(sendlist)) self.sendcommand(sendlist) response = self.getresponse() if debug >= debugneeds: print("readmembyaddr() response: " + hexlist(response)) return response def writemembyaddr(self, writevals): # Function to write to an area of ECU memory. debugneeds = 4 self.writevals = writevals wrmembyaddr = [0x3D] sendlist = wrmembyaddr + self.writevals if debug >= debugneeds: print("writemembyaddr() sendlist: " + hexlist(sendlist)) self.sendcommand(sendlist) response = self.getresponse() if debug >= debugneeds: print("writemembyaddr() response: " + hexlist(response)) return response def testerpresent(self): # KWP2000 TesterPresent command tp = [0x3E] self.sendcommand(tp) response = self.getresponse() return response def setuplogrecord(self, logline): # KWP2000 command to access timing parameters self.logline = logline response = [] sendlist = [0xb7] # is 0xB7 the "locator?" sendlist = sendlist + [0x03] # Number of bytes per field ? sendlist = sendlist + self.logline self.sendcommand(sendlist) response = self.getresponse() return response def getlogrecord(self): # Command to request a logging record gr = [0xb7] self.sendcommand(gr) response = self.getresponse() return response def sendhexstring(self, dumpstring): # Takes a list of characters as a string, turns every two characters into a hex byte and sends it raw. # used as needed for dev/test/debug self.dumpstring = dumpstring for i in range(len(self.dumpstring) / 2): self.send([int('0x' + self.dumpstring[i * 2:(i * 2) + 2], 16)])
class Py64drive: def __init__(self): self.device = Device(device_id="64drive USB device") def _send_cmd(self, cmd, params): txbuf = struct.pack(">B3s",cmd,b"CMD") if len(params) >= 1: txbuf += struct.pack(">L",params[0]) if len(params) >= 2: txbuf += struct.pack(">L",params[1]) self.device.write(txbuf) def _recv_resp(self): rxbuf = self.device.read(4) return struct.unpack(">L", rxbuf) def _recv_ver_resp(self): rxbuf = self.device.read(8) return struct.unpack(">L4s", rxbuf) def _recv_success(self,command): while True: rxbuf = self.device.read(4) if rxbuf: break if rxbuf != b"CMP" + bytes([command]): #print("Got {}, expected CMP{}",rxbuf,command,file=sys.stderr) return False return True def load_image(self, data, bank, ram_addr=0): length = len(data) if length % 512: #print("File was truncated during loading.", file=sys.stderr) length -= length % 512 for offset in range(0, length, CHUNK_SIZE): size = min(CHUNK_SIZE, length - offset) block = data[offset:offset+size] self._send_cmd(0x20, (ram_addr + offset, (bank << 24) | (size & 0xffffff))) self.device.write(block) self._recv_success(0x20) def dump_image(self, length, bank, ram_addr=0): data = b"" if length % 512: #print("File was truncated during dumping.", file=sys.stderr) length -= length % 512 for offset in range(0, length, CHUNK_SIZE): size = min(CHUNK_SIZE, length - offset) self._send_cmd(0x30, (ram_addr + offset, (bank << 24) | (size & 0xffffff))) data += self.device.read(size) self._recv_success(0x30) return data def set_save(self, save_type): self._send_cmd(0x70, (save_type,)) self._recv_success(0x70) def read_version(self): self._send_cmd(0x80,()) val,magic = self._recv_ver_resp() if val == 0 or magic != b"UDEV": #print("Incorrect 64drive version reported.", file=sys.stderr) return False self._recv_success(0x80) return True def pi_write_block(self,*args): raise NotImplementedError("Not implemented yet") def pi_write_block_long(self,*args): raise NotImplementedError("Not implemented yet") def pi_read_int32(self): raise NotImplementedError("Not implemented yet") def pi_write_int32(self,num): raise NotImplementedError("Not implemented yet")
class MDB(object): def __init__(self, deviceid): self.__deviceid = deviceid self.__scaling = 0 self.__coins = {} self.__deposited = 0 def _ftdisend(self, data, mode): data_parity = self._parityOf(int(hexlify(data), 16)) if data_parity == -1: if mode: #parity = serial.PARITY_EVEN parity = 2 else: #parity = serial.PARITY_ODD parity = 1 else: if mode: #parity = serial.PARITY_ODD parity = 1 else: #parity = serial.PARITY_EVEN parity = 2 try: self.__device = Device(self.__deviceid) self.__device.ftdi_fn.ftdi_set_line_property(8, 1, parity) self.__device.baudrate = 9600 self.__device.write(data) self.__device.flush() except pylibftdi.FtdiError: print "FtdiError" self._ftdisend(data, mode) def _parityOf(self, int_type): parity = 0 while (int_type): parity = ~parity int_type = int_type & (int_type - 1) return(parity) def _read(self): returndata = [] for i in self.__device.read(100): returndata.append(i) return returndata def _send(self, data): mode = 1 for element in data: self._ftdisend(element, mode) mode = 0 self._ftdisend(self._calcchk(data), 0) time.sleep(0.1) return self._read() def _calcchk(self, data): sum = 0 for byte in data: sum += int(hexlify(byte), 16) return unhexlify('{:02x}'.format(sum % 256)) def _getbits(self, byte): return bin(int(hexlify(byte), 16))[2:].zfill(8) # CoinChanger def reset(self): print "OUT: Reset" answer = self._send(data = ['\x08']) if (len(answer) == 1) and (answer[0] == '\x00'): print "IN : OK" #self.ack() else: print "IN: Fail" print answer def poll(self): print "OUT: Poll" answer = self._send(data = ['\x0B']) i = 0 while i < len(answer): if answer[i] == '\x00': message = "ACK" elif answer[i] == '\xFF': message = "NACK" elif '\x01' <= answer[i] <= '\x0D': message = { '\x01': 'Escrow request - An escrow lever activation has been detected.', '\x02': 'Changer Payout Busy - The changer is busy activating payout devices', '\x03': 'No Credit - A coin was validated but did not get to the place in the system when credit is given', '\x04': 'Defective Tube Sensor - The changer hasdetected one of the tube sensors behaving abnormally', '\x05': 'Double Arrival - Two coins were detected too close together to validate either one', '\x06': 'Acceptor Unplugged - The changer has detected that the acceptor has been removed', '\x07': 'Tube Jam - A tube payout attempt has resulted in jammed condition', '\x08': 'ROM checksum error - The changers internal checksum does not match the calculated checksum', '\x09': 'Coin Routing Error - A coin has been validated, but did not follow the intended routing', '\x0A': 'Changer Busy - The changer is busy and can not answer a detailed command right now', '\x0B': 'Changer was Reset - The changer has detected an Reset condition and has returned to its power-on idle condition', '\x0C': 'Coin Jam - A coin(s) has jammed in the acceptance path', '\x0D': 'Possible Credited Coin Removal - There has been an attempt to remove a credited coin', }.get(answer[i]) print "IN: " + message elif '\x20' <= answer[i] <= '\x3F': print "IN: Slugs deposited: " + str(int(self._getbits(answer[i])[3:], 2)) elif '\x40' <= answer[i] <= '\x7F': bits = self._getbits(answer[i]) if bits[2:4] == '00': routing = "Cash Box" elif bits[2:4] == '01': routing = "Tubes" elif bits[2:4] == '10': routing = "Not used" elif bits[2:4] == '11': routing = "Rejected" else: routing = "Unknown" cointype = int(bits[4:8], 2) coinsinroutingpath = str(int(self._getbits(answer[i+1]), 2)) print "IN: Coin deposited: Type " + str(cointype) + ", sent to " + routing + ". Now " + coinsinroutingpath + " coins there" self.__deposited += self.__coins[cointype] * self.__scaling i += 1 break; elif '\x80' <= answer[i] <= '\xFE': bits = self._getbits(answer[i]) number = str(int(bits[1:4], 2)) cointype = str(int(bits[4:8], 2)) coinsintube = str(int(self._getbits(answer[i+1]), 2)) print "IN: Coins dispensed manually: " + number + " coins of type " + cointype + ". Now " + coinsintube + " coins there" i += 1 break; else: #\x0E -> \x1F print "IN: Unknown Poll Status reply" + hexlify(answer[i]) i += 1 self.ack() def setup(self): print "OUT: Setup" answer = self._send(data = ['\x09']) if len(answer) == 24: print "IN: Changer Feature Level: " + str(ord(answer[0])) + " (" + hex(ord(answer[0])) + ")" print "IN: Country/Currency-Code: " + hex(ord(answer[1])) + " " + hex(ord(answer[2])) print "IN: Coin Scaling Factor: " + str(ord(answer[3])) + " (" + hex(ord(answer[3])) + ")" self.__scaling = int(ord(answer[3])) print "IN: Decimal Places: " + str(ord(answer[4])) + " (" + hex(ord(answer[4])) + ")" canberoutedtotube = (self._getbits(answer[5]) + self._getbits(answer[6]))[::-1] for i in range(7, 23): print "IN: Coin Type: " + str(i-7) + ", Value: " + str(ord(answer[i])) + ", Can be routed to tube: " + canberoutedtotube[i-7] self.__coins[(i-7)] = int(ord(answer[i])) self.ack() else: print "IN: Fail - " + answer def expansionidentification(self): print "OUT: Expansion Identification" answer = self._send(data = ['\x0F', '\x00']) if len(answer) == 34: print "IN: Manufacturer Code: " + str(answer[0]) + str(answer[1]) + str(answer[2]) + " (" + hex(ord(answer[0])) + " " + hex(ord(answer[1])) + " " + hex(ord(answer[2])) + ")" print "IN: Serial Number: " + ''.join(answer[i] for i in range(3, 15)) + " (" + " ".join(hex(ord(answer[i])) for i in range(3, 15)) + ")" print "IN: Model #/Tuning Revision: " + ''.join(answer[i] for i in range(15, 27)) + " (" + " ".join(hex(ord(answer[i])) for i in range(15, 27)) + ")" print "IN: Software Version: " + hex(ord(answer[27])) + " " + hex(ord(answer[28])) optionalfeatures = (self._getbits(answer[29]) + self._getbits(answer[30]) + self._getbits(answer[31]) + self._getbits(answer[32]))[::-1] print "IN: Optional Feature: Alternative Payout method: " + optionalfeatures[0] print "IN: Optional Feature: Extended Diagnostic command supported: " + optionalfeatures[1] print "IN: Optional Feature: Controlled Manual Fill and Payout commands supported: " + optionalfeatures[2] print "IN: Optional Feature: File Transport Layer (FTL) supported: " + optionalfeatures[3] print "IN: Optional Features: Future extensions: " + optionalfeatures[4:] self.ack() features = [] for i in range (29, 33): features.append(ord(answer[i])) return features else: print "IN: Fail - " + answer def expansionfeatureenable(self, features): print "OUT: Expansion Feature Enable" answer = self._send(data = ['\x0F', '\x01'] + features) if (len(answer) == 1) and (answer[0] == '\x00'): print "IN : OK" self.ack() else: print "IN: Fail - " + answer def expansiondiagnosticstatus(self): print "OUT: Expansion Diagnostic Status" answer = self._send(data = ['\x0F', '\x05']) if len(answer) == 3: print "IN: Main-Code: " + hex(ord(answer[0])) print "IN: Sub-Code: " + hex(ord(answer[1])) message = { '\x01': 'Powering up', '\x02': 'Powering down', '\x03': 'OK', '\x04': 'Keypad shifted', '\x05': '', '\x06': 'Inhibited by VMC', '\x10': '', '\x11': '', '\x12': '', '\x13': '', '\x14': '', '\x15': '', 'unknown': 'Unknown Main-Code', }.get(answer[0], 'unknown') if ( (answer[0] == '\x05') and (answer[1] == '\x10') ): message = "Manual Fill / Payout active" if ( (answer[0] == '\x05') and (answer[1] == '\x20') ): message = "New Inventory Information Available" if answer[0] == '\x10': message = "General changer error: " + { '\x00': 'Non specific error', '\x01': 'Check sum error #1. A check sum error over a particular data range of configuration field detected.', '\x02': 'Check sum error #2. A check sum error over a secondary data range or configuration field detected.', '\x03': 'Low line voltage detected. The changer has disabled acceptance or payout due to a low voltage condition', 'unknown': 'Unknown Sub-Code', }.get(answer[1], 'unknown') if answer[0] == '\x11': message = "Discriminator module error: " + { '\x00': 'Non specific discriminator error.', '\x10': 'Flight deck open.', '\x11': 'Escrow Return stuck open.', '\x30': 'Coin jam in sensor.', '\x41': 'Discrimination below specified standard.', '\x50': 'Validation sensor A out of range. The acceptor detects a problem with sensor A.', '\x51': 'Validation sensor B out of range. The acceptor detects a problem with sensor B.', '\x52': 'Validation sensor C out of range. The acceptor detects a problem with sensor C.', '\x53': 'Operating temperature exceeded. The acceptor detects the ambient temperature has exceeded the changer\'s operating range, thus possibly affecting the acceptance rate.', '\x54': 'Sizing optics failure. The acceptor detects an error in the sizing optics.', 'unknown': 'Unknown Sub-Code', }.get(answer[1], 'unknown') if answer[0] == '\x12': message = "Accept gate module error: " + { '\x00': 'Non specific accept gate error', '\x30': 'Coins entered gate, but did not exit.', '\x31': 'Accept gate alarm active.', '\x40': 'Accept gate open, but no coin detected.', '\x50': 'Post gate sensor covered before gate opened.', 'unknown': 'Unknown Sub-Code', }.get(answer[1], 'unknown') if answer[0] == '\x13': message = "Separator module error: " + { '\x00': 'Non specific separator error', '\x10': 'Sort sensor error. The acceptor detects an error in the sorting sensor', 'unknown': 'Unknown Sub-Code', }.get(answer[1], 'unknown') if answer[0] == '\x14': message = "Dispenser module error: " + { '\x00': 'Non specific dispenser error.', 'unknown': 'Unknown Sub-Code', }.get(answer[1], 'unknown') if answer[0] == '\x15': message = "Coin Cassette / tube module error: " + { '\x00': 'Non specific cassette error.', '\x02': 'Cassette removed.', '\x03': 'Cash box sensor error. The changer detects an error in a cash box sensor.', '\x04': 'Sunlight on tube sensors. The changer detects too much ambient light on one or more of the tube sensors.', 'unknown': 'Unknown Sub-Code', }.get(answer[1], 'unknown') print "IN: Message: " + message self.ack() else: print "IN: Fail - " + answer def tubestatus(self): print "OUT: Tube Status" answer = self._send(data = ['\x0A']) if len(answer) == 19: print "IN: Tube Full Status: " + hex(ord(answer[0])) + " " + hex(ord(answer[1])) for i in range(2, 18): print "IN: Tube Status: " + str(i-2) + " --> " + str(ord(answer[i])) + " (" + hex(ord(answer[i])) + ")" self.ack() else: print "IN: Fail - " + answer def enableall(self, manual = False): if manual == True: self.cointype('\xFF', '\xFF', '\xFF', '\xFF') else: self.cointype('\xFF', '\xFF', '\x00', '\x00') def disableall(self, manual = False): if manual == True: self.cointype('\x00', '\x00', '\xFF', '\xFF') else: self.cointype('\x00', '\x00', '\x00', '\x00') def cointype(self, enable1, enable2, manual1, manual2): print "OUT: Coin type" answer = self._send(data = ['\x0C', enable1, enable2, manual1, manual2]) if (len(answer) == 1) and (answer[0] == '\x00'): print "IN : OK" self.ack() else: print "IN: Fail - " + answer def ack(self): print "OUT: ACK" # ACK, NACK and RET don't take the mode-Bit self._ftdisend('\x00', 0) def payout(self, value): print "OUT: Payout" self._send(data = ['\x0F', '\x02', unhexlify('{:02x}'.format(int(value) / self.__scaling)) ]) def payoutpoll(self): print "OUT: Payout Poll" self._send(data = ['\x0F', '\x04']) def payoutstatus(self): print "OUT: Payout Status" self._send(data = ['\x0F', '\x03']) def getdeposited(self): return self.__deposited def cleardeposited(self): self.__deposited = 0
class Ecu: def __init__(self): self.ser = Device(mode='b', lazy_open=True) def bbang(self, bba): # Take the one-byte address to "bit bang" and bang the port self.bba = bba self.bbser = BitBangDevice() self.bbser.open() self.bbser.direction = 0x01 self.bbser.port = 1 time.sleep(.5) self.bbser.port = 0 outstr = "><" sys.stdout.write('\r' + outstr) sys.stdout.flush() time.sleep(.2) bbbitmask = 1 for i in range(8): if (self.bba[0] & bbbitmask) > 0: outbit = 1 else: outbit = 0 self.bbser.port = outbit outstr = ">" + str(outbit) + outstr[1:] sys.stdout.write('\r' + outstr) sys.stdout.flush() bbbitmask = bbbitmask * 2 time.sleep(.2) self.bbser.port = 1 sys.stdout.write("\n") self.bbser.close() def initialize(self, connect): self.connect = connect if self.connect == "SLOW-0x11": self.ser.close() time.sleep(.5) self.ecuconnect = False while self.ecuconnect == False: print("Attempting ECU connect: " + self.connect ) # Bit bang the K-line bbseq = [ 0x11 ] self.bbang(bbseq) self.ser.open() self.ser.ftdi_fn.ftdi_set_line_property(8, 1, 0) self.ser.baudrate = 10400 self.ser.flush() # Wait for ECU response to bit bang waithex = [ 0x55, 0xef, 0x8f, 1 ] self.waitfor(waithex) # Wait a bit time.sleep(.026) # Send 0x70 self.send([ 0x70 ]) # 0xee means that we're talking to the ECU waithex = [ 0xee, 1 ] response = self.waitfor(waithex) if response[0] == True: self.ecuconnect = True else: print("ECU Connect Failed. Retrying.") def waitfor(self, wf): # This was used for debugging and really is only used for the init at this point. # wf should be a list with the timeout in the last element self.wf = wf isfound = False idx = 0 foundlist = [] capturebytes = [] to = self.wf[-1] timecheck = time.time() while (time.time() <= (timecheck+to)) & (isfound == False): try: recvbyte = self.recvraw(1) if recvbyte != "": recvdata = ord(recvbyte) capturebytes = capturebytes + [ recvdata ] if recvdata == self.wf[idx]: foundlist = foundlist + [ recvdata ] idx = idx + 1 else: foundlist = [] idx = 0 if idx == len(self.wf)-1: isfound = True except: print('error') break return [ isfound, foundlist, capturebytes ] def send(self, sendlist): self.sendlist = sendlist # Puts every byte in the sendlist out the serial port for i in self.sendlist: self.ser.write(chr(i)) def recvraw(self, bytes): self.bytes = bytes recvdata = self.ser.read(self.bytes) return recvdata def recv(self, bytes): self.bytes = bytes isread = False while isread == False: recvbyte = self.ser.read(self.bytes) if recvbyte != "": recvdata = recvbyte isread = True return recvdata def sendcommand(self, sendlist): # Wraps raw KWP command in a length byte and a checksum byte and hands it to send() self.sendlist = sendlist csum = 0 self.sendlist = [len(self.sendlist)] + self.sendlist csum = self.checksum(self.sendlist) self.sendlist = self.sendlist + [csum] self.send(self.sendlist) cmdval = self.commandvalidate(self.sendlist) return cmdval def commandvalidate(self, command): # Every KWP command is echoed back. This clears out these bytes. self.command = command cv = True for i in range(len(self.command)): recvdata = self.recv(1) if ord(recvdata) != self.command[i]: cv = cv & False return cv def checksum(self, checklist): # Calculates the simple checksum for the KWP command bytes. self.checklist = checklist csum = 0 for i in self.checklist: csum = csum + i csum = (csum & 0xFF) % 0xFF return csum def getresponse(self): # gets a properly formated KWP response from a command and returns the data. debugneeds = 4 numbytes = 0 while numbytes == 0: # This is a hack because sometimes responses have leading 0x00's. Why? This removes them. numbytes = ord(self.recv(1)) gr = [ numbytes ] if debug >= debugneeds: print("Get bytes: " + hex(numbytes)) for i in range(numbytes): recvdata = ord(self.recv(1)) if debug >= debugneeds: print("Get byte" + str(i) + ": " + hex(recvdata)) gr = gr + [ recvdata ] checkbyte = self.recv(1) if debug >= debugneeds: print(gr) if debug >= debugneeds: print("GR: " + hex(ord(checkbyte)) + "<-->" + hex(self.checksum(gr))) return (gr + [ ord(checkbyte) ]) def readecuid(self, paramdef): # KWP2000 command to pull the ECU ID self.paramdef = paramdef debugneeds = 4 reqserviceid = [ 0x1A ] sendlist = reqserviceid + self.paramdef if debug >= debugneeds: print( sendlist ) self.sendcommand(sendlist) response = self.getresponse() if debug >= debugneeds: print(response) return response def stopcomm(self): # KWP2000 command to tell the ECU that the communications is finished stopcommunication = [ 0x82 ] self.sendcommand(stopcommunication) response = self.getresponse() return response def startdiagsession(self, bps): # KWP2000 setup that sets the baud for the logging session self.bps = bps startdiagnosticsession = [ 0x10 ] setbaud = [ 0x86 ] #Is this the actual function of 0x86? # if self.bps == 10400: # bpsout = [ 0x?? ] # if self.bps == 14400: # bpsout = [ 0x?? ] if self.bps == 19200: bpsout = [ 0x30 ] if self.bps == 38400: bpsout = [ 0x50 ] if self.bps == 56000: bpsout = [ 0x63 ] if self.bps == 57600: bpsout = [ 0x64 ] # if self.bps == 125000: # bpsout = [ 0x?? ] sendlist = startdiagnosticsession + setbaud + bpsout self.sendcommand(sendlist) response = self.getresponse() self.ser.baudrate = self.bps time.sleep(1) return response def accesstimingparameter(self, params): # KWP2000 command to access timing parameters self.params = params accesstiming_setval = [ 0x083, 0x03 ] accesstiming = accesstiming_setval + self.params sendlist = accesstiming self.sendcommand(sendlist) response = self.getresponse() return response def readmembyaddr(self, readvals): # Function to read an area of ECU memory. debugneeds = 4 self.readvals = readvals rdmembyaddr = [ 0x23 ] sendlist = rdmembyaddr + self.readvals if debug >= debugneeds: print("readmembyaddr() sendlist: " + sendlist) self.sendcommand(sendlist) response = self.getresponse() if debug >= debugneeds: print("readmembyaddr() response: " + response) return response def writemembyaddr(self, writevals): # Function to write to an area of ECU memory. debugneeds = 4 self.writevals = writevals wrmembyaddr = [ 0x3D ] sendlist = wrmembyaddr + self.writevals if debug >= debugneeds: print("writemembyaddr() sendlist: " + sendlist) self.sendcommand(sendlist) response = self.getresponse() if debug >= debugneeds: print("writemembyaddr() response: " + response) return response def testerpresent(self): # KWP2000 TesterPresent command tp = [ 0x3E ] self.sendcommand(tp) response = self.getresponse() return response def setuplogrecord(self, logline): # KWP2000 command to access timing parameters self.logline = logline response = [] sendlist = [ 0xb7 ] # is 0xB7 the "locator?" sendlist = sendlist + [ 0x03 ] # Number of bytes per field ? sendlist = sendlist + self.logline self.sendcommand(sendlist) response = self.getresponse() return response def getlogrecord(self): # Command to request a logging record gr = [ 0xb7 ] self.sendcommand(gr) response = self.getresponse() return response def sendhexstring(self, dumpstring): # Takes a list of characters as a string, turns every two characters into a hex byte and sends it raw. # used as needed for dev/test/debug self.dumpstring = dumpstring for i in range(len(self.dumpstring)/2): self.send([ int('0x'+self.dumpstring[i*2:(i*2)+2],16) ])
try: dev_list = Driver().list_devices() if len(dev_list) != 0: print "\n\nFollowing devices found: \n" for device_ in dev_list: print device_ dev = Device(device_id="FTZ17IRO", mode='b', interface_select=2) dev.open() tx_data = bytearray(range(0, 256)) dev.write(tx_data) rx_data = bytearray(dev.read(257))#, timeout = 0)) if len(rx_data) == 256 : print "\n\n[Test] Test 1 Passed: Sent 256 bytes of data, received 256 bytes of data" failed = False for i in range(256): if ((tx_data[i]+1)%256) != rx_data[i]: print "[Test] Test 2: Data verification failed! , tx_data : ", tx_data[i], " =/= rx_data : ", rx_data[i] failed = True if not(failed): print "[Test] Test 2 Successful: Data transmit and receive verified!" dev.close()
if len(dev_list) != 0: print "Following devices found:" for device_ in dev_list: print device_ dev = Device(device_id="FTZ17IRO", mode='b', interface_select=2) dev.open() epochs = 1024 * 10 BLOCK_LEN = 2048 tx_data = bytearray([random.randrange(0, 256) for i in range(BLOCK_LEN)]) ts = time.time() while epochs: dev.write(tx_data) rx_data = bytearray(dev.read(BLOCK_LEN)) #print "Epoch: {}".format(epochs) failed = False for i in range(BLOCK_LEN): if ((tx_data[i] + 1) % 256) != rx_data[i]: print "Epoch: {}".format(epochs) print "[Test] Test 2: Data verification failed! , tx_data : ", tx_data[ i], " =/= rx_data : ", rx_data[i] failed = True print "Breaking..." break if failed: break epochs -= 1
class FtdiAsyncInterface: ################################################################## # Construction ################################################################## def __init__(self, iface=None): self.interface = iface self.target = None self.prog_cb = None self.CMD_NOP = 0x0 self.CMD_WR = 0x1 self.CMD_RD = 0x2 self.CMD_GP_WR = 0x3 self.CMD_GP_RD = 0x4 self.HDR_SIZE = 6 self.MAX_SIZE = 255 self.BLOCK_SIZE_WR = 16 # Really 2048 self.BLOCK_SIZE_RD = 32 self.MAGIC_ADDR = 0xF0000000 # Default self.dev_id = None self.dev_iface = 2 # User specified (device_id.iface) if iface != None and iface != "": parts = iface.split(".") self.dev_id = parts[0] if len(parts) > 1: self.dev_iface = int(parts[1]) ################################################################## # set_progress_cb: Set progress callback ################################################################## def set_progress_cb(self, prog_cb): self.prog_cb = prog_cb ################################################################## # connect: Open serial connection ################################################################## def connect(self): self.target = Device(device_id=self.dev_id, interface_select=self.dev_iface) ################################################################## # write: Write a block of data to a specified address ################################################################## def write(self, addr, data, length, addr_incr=True, max_block_size=-1): # Connect if required if self.target == None: self.connect() # Write blocks idx = 0 remainder = length if self.prog_cb != None: self.prog_cb(0, length) if max_block_size == -1: max_block_size = self.BLOCK_SIZE_WR while remainder > 0: l = max_block_size if l > remainder: l = remainder cmd = bytearray(2 + 4 + l) cmd[0] = (((l >> 8) & 0xF) << 4) | self.CMD_WR cmd[1] = l & 0xFF cmd[2] = (addr >> 24) & 0xFF cmd[3] = (addr >> 16) & 0xFF cmd[4] = (addr >> 8) & 0xFF cmd[5] = (addr >> 0) & 0xFF for i in range(l): cmd[6 + i] = data[idx] idx += 1 # Write to interface self.target.write(cmd) # Update display if self.prog_cb != None: self.prog_cb(idx, length) if addr_incr: addr += l remainder -= l ################################################################## # read: Read a block of data from a specified address ################################################################## def read(self, addr, length, addr_incr=True, max_block_size=-1): # Connect if required if self.target == None: self.connect() idx = 0 remainder = length data = bytearray(length) if self.prog_cb != None: self.prog_cb(0, length) if max_block_size == -1: max_block_size = self.BLOCK_SIZE_RD while remainder > 0: l = max_block_size if l > remainder: l = remainder cmd = bytearray(2 + 4) cmd[0] = (((l >> 8) & 0xF) << 4) | self.CMD_RD cmd[1] = l & 0xFF cmd[2] = (addr >> 24) & 0xFF cmd[3] = (addr >> 16) & 0xFF cmd[4] = (addr >> 8) & 0xFF cmd[5] = (addr >> 0) & 0xFF # Write to serial port self.target.write(cmd) # Read block response for i in range(l): data[idx] = ord(self.target.read(1)) & 0xFF idx += 1 # Update display if self.prog_cb != None: self.prog_cb(idx, length) if addr_incr: addr += l remainder -= l return data ################################################################## # read32: Read a word from a specified address ################################################################## def read32(self, addr): # Connect if required if self.target == None: self.connect() #if addr == self.MAGIC_ADDR: # return self.read_gpio() # Send read command cmd = bytearray([ self.CMD_RD, 4, (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, (addr >> 0) & 0xFF ]) self.target.write(cmd) value = 0 idx = 0 while (idx < 4): b = self.target.read(1) value |= (ord(b) << (idx * 8)) idx += 1 return value ################################################################## # write32: Write a word to a specified address ################################################################## def write32(self, addr, value): # Connect if required if self.target == None: self.connect() #if addr == self.MAGIC_ADDR: # self.write_gpio(value) # return # Send write command cmd = bytearray([ self.CMD_WR, 4, (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, (addr >> 0) & 0xFF, (value >> 0) & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF ]) self.target.write(cmd) ################################################################## # read_gpio: Read GPIO bus ################################################################## def read_gpio(self): # Connect if required if self.target == None: self.connect() # Send read command cmd = bytearray([self.CMD_GP_RD]) self.target.write(cmd) return ord(self.target.read(1)) ################################################################## # write_gpio: Write a byte to GPIO ################################################################## def write_gpio(self, value): # Connect if required if self.target == None: self.connect() # Send write command cmd = bytearray([self.CMD_GP_WR, (value >> 0) & 0xFF]) self.target.write(cmd)
class Glitcher(): """Simple iCEstick voltage glitcher""" def __init__(self, start_offset=0, end_offset=5000, offset_step=1, duration_step=1, start_duration=1, end_duration=30, retries=2): """Initialize the glitcher""" # set FTDI device for communication with iCEstick self.dev = Device(mode='b', interface_select=INTERFACE_B) # set baudrate self.dev.baudrate = 115200 # set offset and duration steps self.offset_step = offset_step self.duration_step = duration_step self.start_offset = start_offset self.end_offset = end_offset self.start_duration = start_duration self.end_duration = end_duration self.retries = retries def read_data(self, terminator=b"\r\n", echo=True): """Read UART data""" # if echo is on, read the echo first if echo: c = b"\x00" while c != b"\r": c = self.dev.read(1) data = b"" count = 0 while True: count += 1 data += self.dev.read(1) if data[-2:] == CRLF: break if count > MAX_BYTES: return "UART_TIMEOUT" # return read bytes without terminator return data.replace(terminator, b"") def synchronize(self): """UART synchronization with auto baudrate detection""" # use auto baudrate detection cmd = b"?" data = CMD_PASSTHROUGH + pack("B", len(cmd)) + cmd self.dev.write(data) # receive synchronized message resp = self.read_data(echo=False) if resp != SYNCHRONIZED: return False # respond with "Synchronized" cmd = SYNCHRONIZED + CRLF data = CMD_PASSTHROUGH + pack("B", len(cmd)) + cmd self.dev.write(data) # read response, should be "OK" resp = self.read_data() if resp != OK: return False # send crystal frequency (in kHz) self.dev.write(CMD_PASSTHROUGH + b"\x07" + CRYSTAL_FREQ) # read response, should be "OK" resp = self.read_data() if resp != OK: return False return True def read_command_response(self, response_count, echo=True, terminator=b"\r\n"): """Read command response from target device""" result = [] data = b"" # if echo is on, read the sent back ISP command before the actual response count = 0 if echo: c = b"\x00" while c != b"\r": count += 1 c = self.dev.read(1) if count > MAX_BYTES: return "TIMEOUT" # read return code data = b"" old_len = 0 count = 0 while True: data += self.dev.read(1) # if data[len(terminator) * -1:] == terminator: if data[-2:] == terminator: break if len(data) == old_len: count += 1 if count > MAX_BYTES: return "TIMEOUT" else: old_len = len(data) # add return code to result return_code = data.replace(CRLF, b"") result.append(return_code) # check return code and return immediately if it is not "CMD_SUCCESS" if return_code != b"0": return result # read specified number of responses for i in range(response_count): data = b"" count = 0 old_len = 0 while True: data += self.dev.read(1) if data[-2:] == terminator: break if len(data) == old_len: count += 1 if count > MAX_BYTES: return "TIMEOUT" else: old_len = len(data) # add response to result result.append(data.replace(CRLF, b"")) return result def send_target_command(self, command, response_count=0, echo=True, terminator=b"\r\n"): """Send command to target device""" # send command cmd = command + b"\x0d" data = CMD_PASSTHROUGH + pack("B", len(cmd)) + cmd self.dev.write(data) # read response resp = self.read_command_response(response_count, echo, terminator) return resp def reset_target(self): """Reset target device""" # send command self.dev.write(CMD_RESET) def set_glitch_duration(self, duration): """Send config command to set glitch duration in FPGA clock cycles""" # send command data = CMD_SET_DURATION + pack("<L", duration) self.dev.write(data) def set_glitch_offset(self, offset): """Send config command to set glitch offset in FPGA clock cycles""" # send command data = CMD_SET_OFFSET + pack("<L", offset) self.dev.write(data) def start_glitch(self): """Start glitch (actually start the offset counter)""" # send command self.dev.write(CMD_START_GLITCH) def dump_memory(self): """Dump the target device memory""" # dump the 32 kB flash memory and save the content to a file with open(DUMP_FILE, "wb") as f: # read all 32 kB of flash memory for i in range(1023): # first send "OK" to the target device resp = self.send_target_command(OK, 1, True, b"\r\n") # then a read command for 32 bytes cmd = "R {} 32".format(i * 32).encode("utf-8") resp = self.send_target_command(cmd, 1, True, b"\r\n") if resp[0] == b"0": # read and decode uu-encodod data in a somewhat "hacky" way data = b"begin 666 <data>\n" + resp[1] + b" \n \nend\n" raw_data = decode(data, "uu") print(fg.li_blue + bytes.hex(raw_data) + fg.rs) f.write(raw_data) print(fg.li_white + "[*] Dumped memory written to '{}'".format(DUMP_FILE) + fg.rs) def run(self): """Run the glitching process with the current configuration""" # # reset target # self.reset_target() # # # read and show the UID of the target device # print(fg.li_white + "[*] Read target device UID" + fg.rs) # resp = self.send_target_command(b"N", 4, True, b"\r\n") # # if resp[0] == b"0" and len(resp) == 5: # uid = "{} {} {} {}".format(resp[4].decode("ascii"), resp[3].decode("ascii"), resp[2].decode("ascii"), resp[1].decode("ascii")) # else: # uid = "<unknown>" # print(fg.li_red + "[-] Could not read target device UID" + fg.rs) # # # read part identification number # print(fg.li_white + "[*] Read target device part ID" + fg.rs) # resp = self.send_target_command(b"J", 1, True, b"\r\n") # # if resp[0] == b"0": # part_id = "{}".format(resp[1].decode("ascii")) # else: # part_id = "<unknown>" # print(fg.li_red + "[-] Could not read target part ID" + fg.rs) # # # show target device info # print(fg.li_white + "[*] Target device info:\n" + # " UID: {}\n".format(uid) + # " Part identification number: {}".format(part_id)) # # print(fg.li_white + "[*] Press <ENTER> to start the glitching process" + fg.rs) # input() # measure the time start_time = datetime.now() for offset in range(self.start_offset, self.end_offset, self.offset_step): # duration in 10 ns increments for duration in range(self.start_duration, self.end_duration, self.duration_step): # better test more than once for i in range(self.retries): # set glitch config print(fg.li_white + "[*] Set glitch configuration ({},{})".format( offset, duration) + fg.rs) self.set_glitch_offset(offset) self.set_glitch_duration(duration) # start glitch (start the offset counter) self.start_glitch() # reset target device self.reset_target() # synchronize with target if not self.synchronize(): print(fg.li_red + "[-] Error during sychronisation" + fg.rs) continue # read flash memory address resp = self.send_target_command(READ_FLASH_CHECK, 1, True, b"\r\n") if resp[0] == b"0": # measure the time again end_time = datetime.now() print( ef.bold + fg.green + "[*] Glitching success!\n" " Bypassed the readout protection with the following glitch parameters:\n" " offset = {}\n duration = {}\n". format(offset, duration) + " Time to find this glitch: {}".format( end_time - start_time) + fg.rs) # save successful glitching configuration in file config = "{},{},{},{}\n".format( offset, duration, resp[0], resp[1]) with open(RESULTS_FILE, "a") as f: f.write(config) # dump memory print(fg.li_white + "[*] Dumping the flash memory ..." + fg.rs) self.dump_memory() return True elif resp[0] != b"19": print(fg.li_red + "[?] Unexpected response: {}".format(resp) + fg.rs) return False
from pylibftdi import Device import asterisk.manager, binascii, time #'\x01\xe7\x6e' USER_DATA = { '\x01\xe7l\x02': {'nick': 'swiss', 'song': 'custom/youngbuk2'}, '\x01\xe7_Y': {'nick': 'Joule_Thief', 'song': 'custom/smokeonwater'}, '\x01\xe7ma': {'nick': 'elimisteve', 'song': 'custom/loop'}, '\x01\xe7f\x8d': {'nick': 'luckyaba', 'song': 'custom/goodfoot'} } KEY_LENGTH = 6 dev = Device(mode='b') dev.baudrate = 9600 while True: string = dev.read(KEY_LENGTH).strip() old_string = '' # Not yet used to compare string and old_string if string != "": if string in USER_DATA: print "Hex:", binascii.b2a_hex(string) dev.write('U') # Play ringtone/theme song manager = asterisk.manager.Manager() manager.connect('192.168.1.200') manager.login('steve', 'amp111') # manager.originate('', '') data = {"Action": "Originate", "Channel": "SIP/180",
class Ecu: def __init__(self): self.ser = Device(mode='b', lazy_open=True) def bbang(self, bba): # Take the one-byte address to "bit bang" and bang the port self.bba = bba self.bbser = BitBangDevice() self.bbser.open() self.bbser.direction = 0x01 self.bbser.port = 1 time.sleep(.5) self.bbser.port = 0 outstr = "><" sys.stdout.write('\r' + outstr) sys.stdout.flush() time.sleep(.2) bbbitmask = 1 for i in range(8): if (self.bba[0] & bbbitmask) > 0: outbit = 1 else: outbit = 0 self.bbser.port = outbit outstr = ">" + str(outbit) + outstr[1:] sys.stdout.write('\r' + outstr) sys.stdout.flush() bbbitmask = bbbitmask * 2 time.sleep(.2) self.bbser.port = 1 sys.stdout.write("\n") self.bbser.close() def initialize(self, connect): self.connect = connect if self.connect == "SLOW-0x11": self.ser.close() time.sleep(.5) self.ecuconnect = False while self.ecuconnect == False: print("Attempting ECU connect: " + self.connect) # Bit bang the K-line bbseq = [0x11] self.bbang(bbseq) self.ser.open() self.ser.ftdi_fn.ftdi_set_line_property(8, 1, 0) self.ser.baudrate = 10400 self.ser.flush() # Wait for ECU response to bit bang waithex = [0x55, 0xef, 0x8f, 1] self.waitfor(waithex) # Wait a bit time.sleep(.026) # Send 0x70 self.send([0x70]) # 0xee means that we're talking to the ECU waithex = [0xee, 1] response = self.waitfor(waithex) if response[0] == True: self.ecuconnect = True else: print("ECU Connect Failed. Retrying.") def waitfor(self, wf): # This was used for debugging and really is only used for the init at this point. # wf should be a list with the timeout in the last element self.wf = wf isfound = False idx = 0 foundlist = [] capturebytes = [] to = self.wf[-1] timecheck = time.time() while (time.time() <= (timecheck + to)) & (isfound == False): try: recvbyte = self.recvraw(1) if recvbyte != "": recvdata = ord(recvbyte) capturebytes = capturebytes + [recvdata] if recvdata == self.wf[idx]: foundlist = foundlist + [recvdata] idx = idx + 1 else: foundlist = [] idx = 0 if idx == len(self.wf) - 1: isfound = True except: print('error') break return [isfound, foundlist, capturebytes] def send(self, sendlist): self.sendlist = sendlist # Puts every byte in the sendlist out the serial port for i in self.sendlist: self.ser.write(chr(i)) def recvraw(self, bytes): self.bytes = bytes recvdata = self.ser.read(self.bytes) return recvdata def recv(self, bytes): self.bytes = bytes isread = False while isread == False: recvbyte = self.ser.read(self.bytes) if recvbyte != "": recvdata = recvbyte isread = True return recvdata def sendcommand(self, sendlist): # Wraps raw KWP command in a length byte and a checksum byte and hands it to send() self.sendlist = sendlist csum = 0 self.sendlist = [len(self.sendlist)] + self.sendlist csum = self.checksum(self.sendlist) self.sendlist = self.sendlist + [csum] self.send(self.sendlist) cmdval = self.commandvalidate(self.sendlist) return cmdval def commandvalidate(self, command): # Every KWP command is echoed back. This clears out these bytes. self.command = command cv = True for i in range(len(self.command)): recvdata = self.recv(1) if ord(recvdata) != self.command[i]: cv = cv & False return cv def checksum(self, checklist): # Calculates the simple checksum for the KWP command bytes. self.checklist = checklist csum = 0 for i in self.checklist: csum = csum + i csum = (csum & 0xFF) % 0xFF return csum def getresponse(self): # gets a properly formated KWP response from a command and returns the data. debugneeds = 4 numbytes = 0 while numbytes == 0: # This is a hack because sometimes responses have leading 0x00's. Why? This removes them. numbytes = ord(self.recv(1)) gr = [numbytes] if debug >= debugneeds: print("Get bytes: " + hex(numbytes)) for i in range(numbytes): recvdata = ord(self.recv(1)) if debug >= debugneeds: print("Get byte" + str(i) + ": " + hex(recvdata)) gr = gr + [recvdata] checkbyte = self.recv(1) if debug >= debugneeds: print(gr) if debug >= debugneeds: print("GR: " + hex(ord(checkbyte)) + "<-->" + hex(self.checksum(gr))) return (gr + [ord(checkbyte)]) def readecuid(self, paramdef): # KWP2000 command to pull the ECU ID self.paramdef = paramdef debugneeds = 4 reqserviceid = [0x1A] sendlist = reqserviceid + self.paramdef if debug >= debugneeds: print(sendlist) self.sendcommand(sendlist) response = self.getresponse() if debug >= debugneeds: print(response) return response def stopcomm(self): # KWP2000 command to tell the ECU that the communications is finished stopcommunication = [0x82] self.sendcommand(stopcommunication) response = self.getresponse() return response def startdiagsession(self, bps): # KWP2000 setup that sets the baud for the logging session self.bps = bps startdiagnosticsession = [0x10] setbaud = [0x86] #Is this the actual function of 0x86? # if self.bps == 10400: # bpsout = [ 0x?? ] # if self.bps == 14400: # bpsout = [ 0x?? ] if self.bps == 19200: bpsout = [0x30] if self.bps == 38400: bpsout = [0x50] if self.bps == 56000: bpsout = [0x63] if self.bps == 57600: bpsout = [0x64] # if self.bps == 125000: # bpsout = [ 0x?? ] sendlist = startdiagnosticsession + setbaud + bpsout self.sendcommand(sendlist) response = self.getresponse() self.ser.baudrate = self.bps time.sleep(1) return response def accesstimingparameter(self, params): # KWP2000 command to access timing parameters self.params = params accesstiming_setval = [0x083, 0x03] accesstiming = accesstiming_setval + self.params sendlist = accesstiming self.sendcommand(sendlist) response = self.getresponse() return response def readmembyaddr(self, readvals): # Function to read an area of ECU memory. debugneeds = 4 self.readvals = readvals rdmembyaddr = [0x23] sendlist = rdmembyaddr + self.readvals if debug >= debugneeds: print("readmembyaddr() sendlist: " + sendlist) self.sendcommand(sendlist) response = self.getresponse() if debug >= debugneeds: print("readmembyaddr() response: " + response) return response def writemembyaddr(self, writevals): # Function to write to an area of ECU memory. debugneeds = 4 self.writevals = writevals wrmembyaddr = [0x3D] sendlist = wrmembyaddr + self.writevals if debug >= debugneeds: print("writemembyaddr() sendlist: " + sendlist) self.sendcommand(sendlist) response = self.getresponse() if debug >= debugneeds: print("writemembyaddr() response: " + response) return response def testerpresent(self): # KWP2000 TesterPresent command tp = [0x3E] self.sendcommand(tp) response = self.getresponse() return response def setuplogrecord(self, logline): # KWP2000 command to access timing parameters self.logline = logline response = [] sendlist = [0xb7] # is 0xB7 the "locator?" sendlist = sendlist + [0x03] # Number of bytes per field ? sendlist = sendlist + self.logline self.sendcommand(sendlist) response = self.getresponse() return response def getlogrecord(self): # Command to request a logging record gr = [0xb7] self.sendcommand(gr) response = self.getresponse() return response def sendhexstring(self, dumpstring): # Takes a list of characters as a string, turns every two characters into a hex byte and sends it raw. # used as needed for dev/test/debug self.dumpstring = dumpstring for i in range(len(self.dumpstring) / 2): self.send([int('0x' + self.dumpstring[i * 2:(i * 2) + 2], 16)])