mirror of
https://github.com/infiniteCable2/panda.git
synced 2026-02-19 01:33:52 +08:00
SPI: fixes + fake bulk transfers (#1150)
* check spi checkusm * ugh, fix control handler * fake bulk xfer * cleanup * one more * unused * fix linter * some typing Co-authored-by: Comma Device <device@comma.ai>
This commit is contained in:
@@ -8,6 +8,7 @@ import hashlib
|
||||
import datetime
|
||||
import traceback
|
||||
import warnings
|
||||
import logging
|
||||
from functools import wraps
|
||||
from typing import Optional
|
||||
from itertools import accumulate
|
||||
@@ -21,6 +22,11 @@ from .config import DEFAULT_FW_FN, DEFAULT_H7_FW_FN, SECTOR_SIZES_FX, SECTOR_SIZ
|
||||
|
||||
__version__ = '0.0.10'
|
||||
|
||||
# setup logging
|
||||
LOGLEVEL = os.environ.get('LOGLEVEL', 'INFO').upper()
|
||||
logging.basicConfig(level=LOGLEVEL, format='%(message)s')
|
||||
|
||||
|
||||
BASEDIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")
|
||||
|
||||
DEBUG = os.getenv("PANDADEBUG") is not None
|
||||
@@ -50,7 +56,7 @@ def pack_can_buffer(arr):
|
||||
snds.append(b'')
|
||||
idx += 1
|
||||
|
||||
#Apply counter to each 64 byte packet
|
||||
# Apply counter to each 64 byte packet
|
||||
for idx in range(len(snds)):
|
||||
tx = b''
|
||||
counter = 0
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import math
|
||||
import struct
|
||||
import spidev
|
||||
import logging
|
||||
from functools import reduce
|
||||
from typing import List
|
||||
|
||||
# Constants
|
||||
SYNC = 0x5A
|
||||
@@ -11,22 +14,37 @@ CHECKSUM_START = 0xAB
|
||||
|
||||
MAX_RETRY_COUNT = 5
|
||||
|
||||
USB_MAX_SIZE = 0x40
|
||||
|
||||
# This mimics the handle given by libusb1 for easy interoperability
|
||||
class SpiHandle:
|
||||
def __init__(self):
|
||||
self.spi = spidev.SpiDev()
|
||||
self.spi = spidev.SpiDev() # pylint: disable=c-extension-no-member
|
||||
self.spi.open(0, 0)
|
||||
|
||||
self.spi.max_speed_hz = 30000000
|
||||
|
||||
# helpers
|
||||
def _transfer(self, endpoint, data, max_rx_len=1000):
|
||||
for _ in range(MAX_RETRY_COUNT):
|
||||
def _calc_checksum(self, data: List[int]) -> int:
|
||||
cksum = CHECKSUM_START
|
||||
for b in data:
|
||||
cksum ^= b
|
||||
return cksum
|
||||
|
||||
def _transfer(self, endpoint: int, data, max_rx_len: int = 1000) -> bytes:
|
||||
logging.debug("starting transfer: endpoint=%d, max_rx_len=%d", endpoint, max_rx_len)
|
||||
logging.debug("==============================================")
|
||||
|
||||
for n in range(MAX_RETRY_COUNT):
|
||||
logging.debug("\ntry #%d", n+1)
|
||||
try:
|
||||
logging.debug("- send header")
|
||||
packet = struct.pack("<BBHH", SYNC, endpoint, len(data), max_rx_len)
|
||||
packet += bytes([reduce(lambda x, y: x^y, packet) ^ CHECKSUM_START])
|
||||
self.spi.xfer2(packet)
|
||||
|
||||
logging.debug("- waiting for ACK")
|
||||
# TODO: add timeout?
|
||||
dat = b"\x00"
|
||||
while dat[0] not in [HACK, NACK]:
|
||||
dat = self.spi.xfer2(b"\x12")
|
||||
@@ -34,10 +52,12 @@ class SpiHandle:
|
||||
if dat[0] == NACK:
|
||||
raise Exception("Got NACK response for header")
|
||||
|
||||
packet = bytes(data)
|
||||
packet += bytes([reduce(lambda x, y: x^y, packet) ^ CHECKSUM_START])
|
||||
# send data
|
||||
logging.debug("- sending data")
|
||||
packet = bytes([*data, self._calc_checksum(data)])
|
||||
self.spi.xfer2(packet)
|
||||
|
||||
logging.debug("- waiting for ACK")
|
||||
dat = b"\x00"
|
||||
while dat[0] not in [DACK, NACK]:
|
||||
dat = self.spi.xfer2(b"\xab")
|
||||
@@ -45,30 +65,41 @@ class SpiHandle:
|
||||
if dat[0] == NACK:
|
||||
raise Exception("Got NACK response for data")
|
||||
|
||||
response_len = struct.unpack("<H", bytes(self.spi.xfer2(b"\x00" * 2)))[0]
|
||||
# get response length, then response
|
||||
response_len_bytes = bytes(self.spi.xfer2(b"\x00" * 2))
|
||||
response_len = struct.unpack("<H", response_len_bytes)[0]
|
||||
|
||||
logging.debug("- receiving response")
|
||||
dat = bytes(self.spi.xfer2(b"\x00" * (response_len + 1)))
|
||||
# TODO: verify CRC
|
||||
dat = dat[:-1]
|
||||
if self._calc_checksum([DACK, *response_len_bytes, *dat]) != 0:
|
||||
raise Exception("SPI got bad checksum")
|
||||
|
||||
return dat
|
||||
return dat[:-1]
|
||||
except Exception:
|
||||
pass
|
||||
logging.exception("SPI transfer failed, %d retries left", n)
|
||||
raise Exception(f"SPI transaction failed {MAX_RETRY_COUNT} times")
|
||||
|
||||
# libusb1 functions
|
||||
def close(self):
|
||||
self.spi.close()
|
||||
|
||||
def controlWrite(self, request_type, request, value, index, data, timeout=0):
|
||||
return self._transfer(0, struct.pack("<HHHH", request, value, index, 0))
|
||||
def controlWrite(self, request_type: int, request: int, value: int, index: int, data, timeout: int = 0):
|
||||
return self._transfer(0, struct.pack("<BHHH", request, value, index, 0))
|
||||
|
||||
def controlRead(self, request_type, request, value, index, length, timeout=0):
|
||||
return self._transfer(0, struct.pack("<HHHH", request, value, index, length))
|
||||
def controlRead(self, request_type: int, request: int, value: int, index: int, length: int, timeout: int = 0):
|
||||
return self._transfer(0, struct.pack("<BHHH", request, value, index, length))
|
||||
|
||||
# TODO: implement these
|
||||
def bulkWrite(self, endpoint, data, timeout=0):
|
||||
pass
|
||||
# TODO: implement these properly
|
||||
def bulkWrite(self, endpoint: int, data: List[int], timeout: int = 0) -> int:
|
||||
for x in range(math.ceil(len(data) / USB_MAX_SIZE)):
|
||||
self._transfer(endpoint, data[USB_MAX_SIZE*x:USB_MAX_SIZE*(x+1)])
|
||||
return len(data)
|
||||
|
||||
def bulkRead(self, endpoint, data, timeout=0):
|
||||
pass
|
||||
def bulkRead(self, endpoint: int, length: int, timeout: int = 0) -> bytes:
|
||||
ret: List[int] = []
|
||||
for _ in range(math.ceil(length / USB_MAX_SIZE)):
|
||||
d = self._transfer(endpoint, [], max_rx_len=USB_MAX_SIZE)
|
||||
ret += d
|
||||
if len(d) < USB_MAX_SIZE:
|
||||
break
|
||||
return bytes(ret)
|
||||
|
||||
Reference in New Issue
Block a user