2017-04-07 09:11:36 +08:00
|
|
|
# python library to interface with panda
|
2017-06-14 09:50:47 +08:00
|
|
|
from __future__ import print_function
|
|
|
|
import binascii
|
2017-04-07 09:11:36 +08:00
|
|
|
import struct
|
2017-04-29 07:56:40 +08:00
|
|
|
import hashlib
|
2017-04-18 16:12:04 +08:00
|
|
|
import socket
|
2017-04-07 09:11:36 +08:00
|
|
|
import usb1
|
2017-07-18 06:29:31 +08:00
|
|
|
import os
|
|
|
|
import time
|
2017-04-07 09:11:36 +08:00
|
|
|
|
2017-07-18 06:29:31 +08:00
|
|
|
__version__ = '0.0.3'
|
2017-06-14 09:50:47 +08:00
|
|
|
|
2017-07-25 06:16:22 +08:00
|
|
|
BASEDIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")
|
|
|
|
|
2017-05-17 08:01:29 +08:00
|
|
|
def parse_can_buffer(dat):
|
|
|
|
ret = []
|
|
|
|
for j in range(0, len(dat), 0x10):
|
|
|
|
ddat = dat[j:j+0x10]
|
|
|
|
f1, f2 = struct.unpack("II", ddat[0:8])
|
|
|
|
extended = 4
|
|
|
|
if f1 & extended:
|
|
|
|
address = f1 >> 3
|
|
|
|
else:
|
|
|
|
address = f1 >> 21
|
2017-07-15 03:30:34 +08:00
|
|
|
ret.append((address, f2>>16, ddat[8:8+(f2&0xF)], (f2>>4)&0xFF))
|
2017-05-17 08:01:29 +08:00
|
|
|
return ret
|
|
|
|
|
|
|
|
class PandaWifiStreaming(object):
|
|
|
|
def __init__(self, ip="192.168.0.10", port=1338):
|
|
|
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
|
|
self.sock.sendto("hello", (ip, port))
|
|
|
|
self.sock.setblocking(0)
|
|
|
|
self.ip = ip
|
|
|
|
self.port = port
|
|
|
|
|
|
|
|
def can_recv(self):
|
|
|
|
ret = []
|
2017-06-14 09:50:47 +08:00
|
|
|
while True:
|
2017-05-17 08:01:29 +08:00
|
|
|
try:
|
|
|
|
dat, addr = self.sock.recvfrom(0x200*0x10)
|
|
|
|
if addr == (self.ip, self.port):
|
|
|
|
ret += parse_can_buffer(dat)
|
|
|
|
except socket.error:
|
|
|
|
break
|
|
|
|
return ret
|
|
|
|
|
2017-04-18 16:12:04 +08:00
|
|
|
# stupid tunneling of USB over wifi and SPI
|
|
|
|
class WifiHandle(object):
|
|
|
|
def __init__(self, ip="192.168.0.10", port=1337):
|
|
|
|
self.sock = socket.create_connection((ip, port))
|
|
|
|
|
|
|
|
def __recv(self):
|
|
|
|
ret = self.sock.recv(0x44)
|
2017-04-18 22:34:56 +08:00
|
|
|
length = struct.unpack("I", ret[0:4])[0]
|
|
|
|
return ret[4:4+length]
|
2017-04-18 16:12:04 +08:00
|
|
|
|
|
|
|
def controlWrite(self, request_type, request, value, index, data, timeout=0):
|
2017-07-13 02:25:10 +08:00
|
|
|
# ignore data in reply, panda doesn't use it
|
|
|
|
return self.controlRead(request_type, request, value, index, 0, timeout)
|
2017-04-18 16:12:04 +08:00
|
|
|
|
|
|
|
def controlRead(self, request_type, request, value, index, length, timeout=0):
|
2017-04-18 22:34:56 +08:00
|
|
|
self.sock.send(struct.pack("HHBBHHH", 0, 0, request_type, request, value, index, length))
|
2017-04-18 16:12:04 +08:00
|
|
|
return self.__recv()
|
|
|
|
|
|
|
|
def bulkWrite(self, endpoint, data, timeout=0):
|
2017-06-14 09:50:47 +08:00
|
|
|
if len(data) > 0x10:
|
|
|
|
raise ValueError("Data must not be longer than 0x10")
|
2017-04-18 22:34:56 +08:00
|
|
|
self.sock.send(struct.pack("HH", endpoint, len(data))+data)
|
2017-04-18 16:12:04 +08:00
|
|
|
self.__recv() # to /dev/null
|
|
|
|
|
|
|
|
def bulkRead(self, endpoint, length, timeout=0):
|
2017-04-18 22:34:56 +08:00
|
|
|
self.sock.send(struct.pack("HH", endpoint, 0))
|
2017-04-18 16:12:04 +08:00
|
|
|
return self.__recv()
|
|
|
|
|
2017-04-29 08:54:23 +08:00
|
|
|
def close(self):
|
|
|
|
self.sock.close()
|
|
|
|
|
2017-04-07 09:11:36 +08:00
|
|
|
class Panda(object):
|
2017-07-18 06:29:31 +08:00
|
|
|
SAFETY_NOOUTPUT = 0
|
|
|
|
SAFETY_HONDA = 1
|
|
|
|
SAFETY_ALLOUTPUT = 0x1337
|
|
|
|
|
2017-07-19 03:15:19 +08:00
|
|
|
SERIAL_DEBUG = 0
|
|
|
|
SERIAL_ESP = 1
|
|
|
|
SERIAL_LIN1 = 2
|
|
|
|
SERIAL_LIN2 = 3
|
|
|
|
|
2017-07-19 12:05:09 +08:00
|
|
|
GMLAN_CAN2 = 1
|
|
|
|
GMLAN_CAN3 = 2
|
|
|
|
|
2017-07-18 09:59:16 +08:00
|
|
|
REQUEST_IN = usb1.ENDPOINT_IN | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE
|
2017-07-18 09:58:04 +08:00
|
|
|
REQUEST_OUT = usb1.ENDPOINT_OUT | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE
|
2017-06-14 09:50:47 +08:00
|
|
|
|
2017-04-07 09:11:36 +08:00
|
|
|
def __init__(self, serial=None, claim=True):
|
2017-07-15 03:30:34 +08:00
|
|
|
self._serial = serial
|
2017-07-25 06:16:22 +08:00
|
|
|
self._handle = None
|
|
|
|
self.connect(claim)
|
|
|
|
|
|
|
|
def close(self):
|
|
|
|
self._handle.close()
|
|
|
|
self._handle = None
|
|
|
|
|
2017-07-30 08:21:22 +08:00
|
|
|
def connect(self, claim=True, wait=False):
|
2017-07-25 06:16:22 +08:00
|
|
|
if self._handle != None:
|
|
|
|
self.close()
|
|
|
|
|
|
|
|
if self._serial == "WIFI":
|
2017-06-21 08:40:24 +08:00
|
|
|
self._handle = WifiHandle()
|
2017-06-14 09:50:47 +08:00
|
|
|
print("opening WIFI device")
|
2017-07-30 23:26:48 +08:00
|
|
|
self.wifi = True
|
2017-04-18 16:12:04 +08:00
|
|
|
else:
|
|
|
|
context = usb1.USBContext()
|
2017-06-21 08:40:24 +08:00
|
|
|
self._handle = None
|
2017-07-30 23:26:48 +08:00
|
|
|
self.wifi = False
|
2017-04-07 09:11:36 +08:00
|
|
|
|
2017-07-30 08:21:22 +08:00
|
|
|
while 1:
|
|
|
|
try:
|
|
|
|
for device in context.getDeviceList(skip_on_error=True):
|
|
|
|
#print(device)
|
|
|
|
if device.getVendorID() == 0xbbaa and device.getProductID() in [0xddcc, 0xddee]:
|
|
|
|
if self._serial is None or device.getSerialNumber() == self._serial:
|
|
|
|
print("opening device", device.getSerialNumber())
|
|
|
|
self.bootstub = device.getProductID() == 0xddee
|
|
|
|
self.legacy = (device.getbcdDevice() != 0x2300)
|
|
|
|
self._handle = device.open()
|
|
|
|
if claim:
|
|
|
|
self._handle.claimInterface(0)
|
|
|
|
#self._handle.setInterfaceAltSetting(0, 0) #Issue in USB stack
|
|
|
|
break
|
|
|
|
except Exception as e:
|
|
|
|
print("exception", e)
|
|
|
|
if wait == False or self._handle != None:
|
|
|
|
break
|
2017-07-25 06:16:22 +08:00
|
|
|
assert(self._handle != None)
|
2017-07-30 08:21:22 +08:00
|
|
|
print("connected")
|
2017-07-25 06:16:22 +08:00
|
|
|
|
2017-07-28 05:29:07 +08:00
|
|
|
def reset(self, enter_bootstub=False, enter_bootloader=False):
|
2017-07-25 06:16:22 +08:00
|
|
|
# reset
|
|
|
|
try:
|
2017-07-28 05:29:07 +08:00
|
|
|
if enter_bootloader:
|
|
|
|
self._handle.controlWrite(Panda.REQUEST_IN, 0xd1, 0, 0, b'')
|
2017-07-25 06:16:22 +08:00
|
|
|
else:
|
2017-07-28 05:29:07 +08:00
|
|
|
if enter_bootstub:
|
|
|
|
self._handle.controlWrite(Panda.REQUEST_IN, 0xd1, 1, 0, b'')
|
|
|
|
else:
|
|
|
|
self._handle.controlWrite(Panda.REQUEST_IN, 0xd8, 0, 0, b'')
|
2017-07-25 06:16:22 +08:00
|
|
|
except Exception:
|
|
|
|
pass
|
2017-07-28 05:29:07 +08:00
|
|
|
if not enter_bootloader:
|
2017-07-30 09:16:08 +08:00
|
|
|
time.sleep(0.5)
|
|
|
|
self.connect()
|
2017-07-25 06:16:22 +08:00
|
|
|
|
2017-07-30 08:21:22 +08:00
|
|
|
def flash(self, fn=None):
|
2017-07-25 06:16:22 +08:00
|
|
|
if not self.bootstub:
|
2017-07-28 05:29:07 +08:00
|
|
|
self.reset(enter_bootstub=True)
|
2017-07-25 06:16:22 +08:00
|
|
|
assert(self.bootstub)
|
|
|
|
|
2017-07-30 08:21:22 +08:00
|
|
|
if fn is None:
|
|
|
|
ret = os.system("cd %s && make clean && make -f %s bin" % (os.path.join(BASEDIR, "board"),
|
|
|
|
"Makefile.legacy" if self.legacy else "Makefile"))
|
|
|
|
fn = os.path.join(BASEDIR, "board", "obj", "code.bin" if self.legacy else "panda.bin")
|
|
|
|
|
|
|
|
with open(fn) as f:
|
2017-07-25 06:16:22 +08:00
|
|
|
dat = f.read()
|
|
|
|
|
2017-07-25 06:41:33 +08:00
|
|
|
# confirm flasher is present
|
2017-07-25 06:16:22 +08:00
|
|
|
fr = self._handle.controlRead(Panda.REQUEST_IN, 0xb0, 0, 0, 0xc)
|
2017-07-25 06:41:33 +08:00
|
|
|
assert fr[4:8] == "\xde\xad\xd0\x0d"
|
2017-07-25 06:16:22 +08:00
|
|
|
|
|
|
|
# unlock flash
|
|
|
|
print("flash: unlocking")
|
|
|
|
self._handle.controlWrite(Panda.REQUEST_IN, 0xb1, 0, 0, b'')
|
|
|
|
|
|
|
|
# erase sectors 1 and 2
|
|
|
|
print("flash: erasing")
|
|
|
|
self._handle.controlWrite(Panda.REQUEST_IN, 0xb2, 1, 0, b'')
|
|
|
|
self._handle.controlWrite(Panda.REQUEST_IN, 0xb2, 2, 0, b'')
|
|
|
|
|
|
|
|
# flash over EP2
|
2017-07-25 06:41:33 +08:00
|
|
|
STEP = 0x10
|
2017-07-25 06:16:22 +08:00
|
|
|
print("flash: flashing")
|
2017-07-25 06:41:33 +08:00
|
|
|
for i in range(0, len(dat), STEP):
|
|
|
|
self._handle.bulkWrite(2, dat[i:i+STEP])
|
2017-07-25 06:16:22 +08:00
|
|
|
|
|
|
|
# reset
|
|
|
|
print("flash: resetting")
|
|
|
|
self.reset()
|
2017-04-07 09:11:36 +08:00
|
|
|
|
2017-07-18 06:29:31 +08:00
|
|
|
@staticmethod
|
2017-07-30 22:59:14 +08:00
|
|
|
def recover(legacy=False):
|
|
|
|
ret = os.system("cd %s && make clean && make -f %s recover" % (os.path.join(BASEDIR, "board"), "Makefile.legacy" if legacy else "Makefile"))
|
2017-07-18 06:29:31 +08:00
|
|
|
time.sleep(1)
|
2017-07-18 13:44:02 +08:00
|
|
|
return ret==0
|
2017-07-18 06:29:31 +08:00
|
|
|
|
2017-04-07 09:11:36 +08:00
|
|
|
@staticmethod
|
|
|
|
def list():
|
|
|
|
context = usb1.USBContext()
|
|
|
|
ret = []
|
|
|
|
for device in context.getDeviceList(skip_on_error=True):
|
|
|
|
if device.getVendorID() == 0xbbaa and device.getProductID() == 0xddcc:
|
|
|
|
ret.append(device.getSerialNumber())
|
2017-04-18 16:12:04 +08:00
|
|
|
# TODO: detect if this is real
|
|
|
|
#ret += ["WIFI"]
|
2017-04-07 09:11:36 +08:00
|
|
|
return ret
|
|
|
|
|
2017-07-19 03:29:16 +08:00
|
|
|
def call_control_api(self, msg):
|
|
|
|
self._handle.controlWrite(Panda.REQUEST_OUT, msg, 0, 0, b'')
|
|
|
|
|
2017-04-07 09:11:36 +08:00
|
|
|
# ******************* health *******************
|
|
|
|
|
|
|
|
def health(self):
|
2017-07-18 09:59:16 +08:00
|
|
|
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd2, 0, 0, 13)
|
2017-04-07 09:11:36 +08:00
|
|
|
a = struct.unpack("IIBBBBB", dat)
|
|
|
|
return {"voltage": a[0], "current": a[1],
|
|
|
|
"started": a[2], "controls_allowed": a[3],
|
|
|
|
"gas_interceptor_detected": a[4],
|
|
|
|
"started_signal_detected": a[5],
|
|
|
|
"started_alt": a[6]}
|
|
|
|
|
2017-04-29 07:56:40 +08:00
|
|
|
# ******************* control *******************
|
|
|
|
|
|
|
|
def enter_bootloader(self):
|
|
|
|
try:
|
2017-07-18 09:58:04 +08:00
|
|
|
self._handle.controlWrite(Panda.REQUEST_OUT, 0xd1, 0, 0, b'')
|
2017-06-14 09:50:47 +08:00
|
|
|
except Exception as e:
|
|
|
|
print(e)
|
2017-04-29 07:56:40 +08:00
|
|
|
pass
|
|
|
|
|
|
|
|
def get_serial(self):
|
2017-07-18 09:59:16 +08:00
|
|
|
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd0, 0, 0, 0x20)
|
2017-06-14 09:50:47 +08:00
|
|
|
hashsig, calc_hash = dat[0x1c:], hashlib.sha1(dat[0:0x1c]).digest()[0:4]
|
2017-07-18 10:43:30 +08:00
|
|
|
assert(hashsig == calc_hash)
|
2017-04-29 07:56:40 +08:00
|
|
|
return [dat[0:0x10], dat[0x10:0x10+10]]
|
|
|
|
|
2017-04-29 08:49:55 +08:00
|
|
|
def get_secret(self):
|
2017-07-18 09:59:16 +08:00
|
|
|
return self._handle.controlRead(Panda.REQUEST_IN, 0xd0, 1, 0, 0x10)
|
2017-04-29 08:49:55 +08:00
|
|
|
|
2017-04-26 06:16:23 +08:00
|
|
|
# ******************* configuration *******************
|
2017-04-07 09:11:36 +08:00
|
|
|
|
2017-07-18 06:29:31 +08:00
|
|
|
def set_esp_power(self, on):
|
2017-07-18 09:58:04 +08:00
|
|
|
self._handle.controlWrite(Panda.REQUEST_OUT, 0xd9, int(on), 0, b'')
|
2017-07-15 12:17:32 +08:00
|
|
|
|
2017-07-18 06:29:31 +08:00
|
|
|
def set_safety_mode(self, mode=SAFETY_NOOUTPUT):
|
2017-07-18 09:58:04 +08:00
|
|
|
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdc, mode, 0, b'')
|
2017-07-15 12:59:23 +08:00
|
|
|
|
|
|
|
def set_can_forwarding(self, from_bus, to_bus):
|
2017-07-18 06:29:31 +08:00
|
|
|
# TODO: This feature may not work correctly with saturated buses
|
2017-07-18 09:58:04 +08:00
|
|
|
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdd, from_bus, to_bus, b'')
|
2017-05-02 14:40:49 +08:00
|
|
|
|
2017-07-19 12:05:09 +08:00
|
|
|
def set_gmlan(self, bus=2):
|
|
|
|
if bus is None:
|
|
|
|
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdb, 0, 0, b'')
|
|
|
|
elif bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]:
|
|
|
|
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdb, 1, bus, b'')
|
2017-04-07 09:11:36 +08:00
|
|
|
|
2017-07-15 11:25:13 +08:00
|
|
|
def set_can_loopback(self, enable):
|
2017-07-18 06:29:31 +08:00
|
|
|
# set can loopback mode for all buses
|
2017-07-18 09:58:04 +08:00
|
|
|
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe5, int(enable), 0, b'')
|
2017-07-15 11:25:13 +08:00
|
|
|
|
2017-07-18 10:43:30 +08:00
|
|
|
def set_can_speed_kbps(self, bus, speed):
|
|
|
|
self._handle.controlWrite(Panda.REQUEST_OUT, 0xde, bus, int(speed*10), b'')
|
|
|
|
|
2017-04-26 06:16:23 +08:00
|
|
|
def set_uart_baud(self, uart, rate):
|
2017-07-18 09:58:04 +08:00
|
|
|
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe1, uart, rate, b'')
|
2017-04-26 06:16:23 +08:00
|
|
|
|
|
|
|
def set_uart_parity(self, uart, parity):
|
|
|
|
# parity, 0=off, 1=even, 2=odd
|
2017-07-18 09:58:04 +08:00
|
|
|
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe2, uart, parity, b'')
|
2017-04-26 06:16:23 +08:00
|
|
|
|
2017-04-26 11:23:05 +08:00
|
|
|
def set_uart_callback(self, uart, install):
|
2017-07-18 09:58:04 +08:00
|
|
|
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe3, uart, int(install), b'')
|
2017-04-26 11:23:05 +08:00
|
|
|
|
2017-04-26 06:16:23 +08:00
|
|
|
# ******************* can *******************
|
|
|
|
|
2017-04-07 09:11:36 +08:00
|
|
|
def can_send_many(self, arr):
|
|
|
|
snds = []
|
2017-06-14 09:50:47 +08:00
|
|
|
transmit = 1
|
|
|
|
extended = 4
|
2017-04-07 09:11:36 +08:00
|
|
|
for addr, _, dat, bus in arr:
|
2017-06-26 14:03:08 +08:00
|
|
|
assert len(dat) <= 8
|
2017-05-08 15:18:50 +08:00
|
|
|
if addr >= 0x800:
|
|
|
|
rir = (addr << 3) | transmit | extended
|
|
|
|
else:
|
|
|
|
rir = (addr << 21) | transmit
|
|
|
|
snd = struct.pack("II", rir, len(dat) | (bus << 4)) + dat
|
2017-06-14 09:50:47 +08:00
|
|
|
snd = snd.ljust(0x10, b'\x00')
|
2017-04-07 09:11:36 +08:00
|
|
|
snds.append(snd)
|
|
|
|
|
2017-06-14 09:50:47 +08:00
|
|
|
while True:
|
2017-04-07 09:11:36 +08:00
|
|
|
try:
|
2017-07-18 06:29:31 +08:00
|
|
|
#print("DAT: %s"%b''.join(snds).__repr__())
|
2017-07-30 23:26:48 +08:00
|
|
|
if self.wifi:
|
|
|
|
for s in snds:
|
|
|
|
self._handle.bulkWrite(3, s)
|
|
|
|
else:
|
|
|
|
self._handle.bulkWrite(3, b''.join(snds))
|
2017-04-07 09:11:36 +08:00
|
|
|
break
|
2017-06-14 09:50:47 +08:00
|
|
|
except (usb1.USBErrorIO, usb1.USBErrorOverflow):
|
|
|
|
print("CAN: BAD SEND MANY, RETRYING")
|
2017-04-07 09:11:36 +08:00
|
|
|
|
|
|
|
def can_send(self, addr, dat, bus):
|
|
|
|
self.can_send_many([[addr, None, dat, bus]])
|
|
|
|
|
|
|
|
def can_recv(self):
|
2017-06-14 09:50:47 +08:00
|
|
|
dat = bytearray()
|
|
|
|
while True:
|
2017-04-07 09:11:36 +08:00
|
|
|
try:
|
2017-06-21 08:40:24 +08:00
|
|
|
dat = self._handle.bulkRead(1, 0x10*256)
|
2017-04-07 09:11:36 +08:00
|
|
|
break
|
2017-06-14 09:50:47 +08:00
|
|
|
except (usb1.USBErrorIO, usb1.USBErrorOverflow):
|
|
|
|
print("CAN: BAD RECV, RETRYING")
|
2017-05-17 08:01:29 +08:00
|
|
|
return parse_can_buffer(dat)
|
2017-04-07 09:11:36 +08:00
|
|
|
|
|
|
|
# ******************* serial *******************
|
|
|
|
|
|
|
|
def serial_read(self, port_number):
|
2017-07-19 03:15:19 +08:00
|
|
|
ret = []
|
|
|
|
while 1:
|
|
|
|
lret = bytes(self._handle.controlRead(Panda.REQUEST_IN, 0xe0, port_number, 0, 0x40))
|
|
|
|
if len(lret) == 0:
|
|
|
|
break
|
|
|
|
ret.append(lret)
|
|
|
|
return b''.join(ret)
|
2017-04-07 09:11:36 +08:00
|
|
|
|
2017-04-18 16:12:04 +08:00
|
|
|
def serial_write(self, port_number, ln):
|
2017-06-21 08:40:24 +08:00
|
|
|
return self._handle.bulkWrite(2, chr(port_number) + ln)
|
2017-04-07 09:11:36 +08:00
|
|
|
|
|
|
|
# ******************* kline *******************
|
|
|
|
|
|
|
|
# pulse low for wakeup
|
|
|
|
def kline_wakeup(self):
|
2017-07-18 09:58:04 +08:00
|
|
|
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf0, 0, 0, b'')
|
2017-04-07 09:11:36 +08:00
|
|
|
|
|
|
|
def kline_drain(self, bus=2):
|
|
|
|
# drain buffer
|
2017-06-14 09:50:47 +08:00
|
|
|
bret = bytearray()
|
|
|
|
while True:
|
2017-07-18 09:59:16 +08:00
|
|
|
ret = self._handle.controlRead(Panda.REQUEST_IN, 0xe0, bus, 0, 0x40)
|
2017-04-07 09:11:36 +08:00
|
|
|
if len(ret) == 0:
|
|
|
|
break
|
2017-06-14 09:50:47 +08:00
|
|
|
bret += ret
|
2017-06-29 05:50:00 +08:00
|
|
|
return bytes(bret)
|
2017-04-07 09:11:36 +08:00
|
|
|
|
|
|
|
def kline_ll_recv(self, cnt, bus=2):
|
2017-06-14 09:50:47 +08:00
|
|
|
echo = bytearray()
|
2017-04-07 09:11:36 +08:00
|
|
|
while len(echo) != cnt:
|
2017-07-18 09:58:04 +08:00
|
|
|
echo += self._handle.controlRead(Panda.REQUEST_OUT, 0xe0, bus, 0, cnt-len(echo))
|
2017-04-07 09:11:36 +08:00
|
|
|
return echo
|
|
|
|
|
|
|
|
def kline_send(self, x, bus=2, checksum=True):
|
|
|
|
def get_checksum(dat):
|
|
|
|
result = 0
|
|
|
|
result += sum(map(ord, dat))
|
|
|
|
result = -result
|
|
|
|
return chr(result&0xFF)
|
|
|
|
|
|
|
|
self.kline_drain(bus=bus)
|
|
|
|
if checksum:
|
|
|
|
x += get_checksum(x)
|
2017-04-18 22:34:56 +08:00
|
|
|
for i in range(0, len(x), 0xf):
|
|
|
|
ts = x[i:i+0xf]
|
2017-06-28 10:47:53 +08:00
|
|
|
self._handle.bulkWrite(2, chr(bus).encode()+ts)
|
2017-04-07 09:11:36 +08:00
|
|
|
echo = self.kline_ll_recv(len(ts), bus=bus)
|
|
|
|
if echo != ts:
|
2017-06-14 09:50:47 +08:00
|
|
|
print("**** ECHO ERROR %d ****" % i)
|
|
|
|
print(binascii.hexlify(echo))
|
|
|
|
print(binascii.hexlify(ts))
|
2017-04-07 09:11:36 +08:00
|
|
|
assert echo == ts
|
|
|
|
|
|
|
|
def kline_recv(self, bus=2):
|
|
|
|
msg = self.kline_ll_recv(2, bus=bus)
|
|
|
|
msg += self.kline_ll_recv(ord(msg[1])-2, bus=bus)
|
|
|
|
return msg
|
2017-07-18 09:58:04 +08:00
|
|
|
|