mirror of
https://jihulab.com/mr-one/openpilot.git
synced 2026-10-27 21:13:42 +08:00
test: use pristine upstream panda SPI firmware (stage 2d)
This commit is contained in:
@@ -121,8 +121,6 @@ ensure_health_packet_version = partial(ensure_version, "health", "HEALTH_PACKET_
|
||||
|
||||
class Panda:
|
||||
|
||||
SERIAL_DEBUG = 0
|
||||
SERIAL_SOM_DEBUG = 4
|
||||
|
||||
USB_VIDS = (0xbbaa, 0x3801) # 0x3801 is comma's registered VID
|
||||
USB_PIDS = (0xddee, 0xddcc)
|
||||
@@ -697,16 +695,6 @@ class Panda:
|
||||
def set_canfd_auto(self, bus, auto):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe8, bus, int(auto), b'')
|
||||
|
||||
def set_uart_baud(self, uart, rate):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe4, uart, int(rate / 300), b'')
|
||||
|
||||
def set_uart_parity(self, uart, parity):
|
||||
# parity, 0=off, 1=even, 2=odd
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe2, uart, parity, b'')
|
||||
|
||||
def set_uart_callback(self, uart, install):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe3, uart, int(install), b'')
|
||||
|
||||
# ******************* can *******************
|
||||
|
||||
# The panda will NAK CAN writes when there is CAN congestion.
|
||||
@@ -752,24 +740,15 @@ class Panda:
|
||||
"""
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf1, bus, 0, b'')
|
||||
|
||||
# ******************* serial *******************
|
||||
|
||||
def serial_read(self, port_number, maxlen=1024):
|
||||
ret = b''
|
||||
while 1:
|
||||
r = bytes(self._handle.controlRead(Panda.REQUEST_IN, 0xe0, port_number, 0, 0x40))
|
||||
if len(r) == 0 or len(ret) >= maxlen:
|
||||
def debug_read(self, maxlen=1024):
|
||||
"""Read up to maxlen bytes of internal debug logs."""
|
||||
ret = bytearray()
|
||||
while len(ret) < maxlen:
|
||||
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xb6, 0, 0, min(0x40, maxlen - len(ret)))
|
||||
if not dat:
|
||||
break
|
||||
ret += r
|
||||
return ret
|
||||
|
||||
def serial_write(self, port_number, ln):
|
||||
ret = 0
|
||||
if isinstance(ln, str):
|
||||
ln = bytes(ln, 'utf-8')
|
||||
for i in range(0, len(ln), 0x20):
|
||||
ret += self._handle.bulkWrite(2, struct.pack("B", port_number) + ln[i:i + 0x20])
|
||||
return ret
|
||||
ret.extend(dat)
|
||||
return bytes(ret)
|
||||
|
||||
def send_heartbeat(self, engaged=True):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf3, engaged, 0, b'')
|
||||
|
||||
+83
-36
@@ -1,4 +1,5 @@
|
||||
import binascii
|
||||
import ctypes
|
||||
import os
|
||||
import math
|
||||
import time
|
||||
@@ -11,24 +12,11 @@ from .base import BaseHandle, BaseSTBootloaderHandle, TIMEOUT
|
||||
from .constants import McuType, MCU_TYPE_BY_IDCODE
|
||||
from .utils import logger
|
||||
|
||||
# No fcntl on Windows
|
||||
try:
|
||||
import fcntl
|
||||
except ImportError:
|
||||
fcntl = None # type: ignore
|
||||
|
||||
# No spidev on MacOS/Windows
|
||||
try:
|
||||
import spidev
|
||||
except ImportError:
|
||||
spidev = None
|
||||
|
||||
# Constants
|
||||
SYNC = 0x5A
|
||||
HACK = 0x79
|
||||
DACK = 0x85
|
||||
NACK = 0x1F
|
||||
CHECKSUM_START = 0xAB
|
||||
# Protocol, see board/drivers/spi.h
|
||||
SLAVE_READY = 0x3C
|
||||
ACK = 0xC3
|
||||
NACK = 0x5A
|
||||
INFO_ENDPOINT = 0xF0
|
||||
|
||||
REQ_HEADER = struct.Struct("<BIHH") # endpoint, id, tx_len, rx_len
|
||||
RESP_HEADER = struct.Struct("<BIH") # status, id, len
|
||||
@@ -66,40 +54,99 @@ class PandaSpiMissingAck(PandaSpiException):
|
||||
SPI_LOCK = threading.Lock()
|
||||
SPI_DEVICES = {}
|
||||
class SpiDevice:
|
||||
"""
|
||||
Provides locked, thread-safe access to a panda's SPI interface.
|
||||
"""Provides locked, thread-safe access to a panda's SPI interface.
|
||||
|
||||
xfer2 returns a view valid until the next transfer; xfer returns an owned copy.
|
||||
"""
|
||||
|
||||
MAX_SPEED = 50000000 # max of the SDM845
|
||||
|
||||
def __init__(self, speed=MAX_SPEED):
|
||||
assert speed <= self.MAX_SPEED
|
||||
# Linux asm-generic ioctl ABI (including aarch64 and x86_64), linux/spi/spidev.h.
|
||||
SPI_IOC_RD_BITS_PER_WORD = 0x80016B03
|
||||
SPI_IOC_WR_MAX_SPEED_HZ = 0x40046B04
|
||||
SPI_IOC_MESSAGE_1 = 0x40206B00
|
||||
SPI_IOC_TRANSFER = struct.Struct('=QQIIHBBBBBB')
|
||||
MAX_TRANSFER_SIZE = 4096
|
||||
|
||||
def __init__(self, speed=MAX_SPEED):
|
||||
try:
|
||||
import fcntl
|
||||
except ImportError as e:
|
||||
raise PandaSpiUnavailable("SPI requires Linux") from e
|
||||
|
||||
assert speed <= self.MAX_SPEED
|
||||
if not os.path.exists(DEV_PATH):
|
||||
raise PandaSpiUnavailable(f"SPI device not found: {DEV_PATH}")
|
||||
if spidev is None:
|
||||
raise PandaSpiUnavailable("spidev is not installed")
|
||||
|
||||
self._ioctl = fcntl.ioctl
|
||||
self._flock = fcntl.flock
|
||||
self._lock_ex, self._lock_un = fcntl.LOCK_EX, fcntl.LOCK_UN
|
||||
with SPI_LOCK:
|
||||
if speed not in SPI_DEVICES:
|
||||
SPI_DEVICES[speed] = spidev.SpiDev()
|
||||
SPI_DEVICES[speed].open(0, 0)
|
||||
SPI_DEVICES[speed].max_speed_hz = speed
|
||||
self._spidev = SPI_DEVICES[speed]
|
||||
file = open(DEV_PATH, 'r+b', buffering=0)
|
||||
try:
|
||||
self._ioctl(file.fileno(), self.SPI_IOC_WR_MAX_SPEED_HZ, struct.pack('=I', speed))
|
||||
bits = self._ioctl(file.fileno(), self.SPI_IOC_RD_BITS_PER_WORD, b'\x00')[0]
|
||||
except BaseException:
|
||||
file.close()
|
||||
raise
|
||||
SPI_DEVICES[speed] = (file, bits)
|
||||
self._file, bits = SPI_DEVICES[speed]
|
||||
|
||||
self._fd = self._file.fileno()
|
||||
self._buffer = bytearray(self.MAX_TRANSFER_SIZE)
|
||||
self._view = memoryview(self._buffer)
|
||||
address = ctypes.addressof(ctypes.c_char.from_buffer(self._buffer))
|
||||
# The kernel supports using the same buffer for transmit and receive.
|
||||
self._transfer = bytearray(self.SPI_IOC_TRANSFER.pack(address, address, 0, speed, 0, bits, 0, 0, 0, 0, 0))
|
||||
self._transfer_words = memoryview(self._transfer).cast('I')
|
||||
|
||||
@contextmanager
|
||||
def acquire(self):
|
||||
try:
|
||||
SPI_LOCK.acquire()
|
||||
fcntl.flock(self._spidev, fcntl.LOCK_EX)
|
||||
yield self._spidev
|
||||
finally:
|
||||
fcntl.flock(self._spidev, fcntl.LOCK_UN)
|
||||
SPI_LOCK.release()
|
||||
with SPI_LOCK:
|
||||
self._flock(self._fd, self._lock_ex)
|
||||
try:
|
||||
yield self
|
||||
finally:
|
||||
self._flock(self._fd, self._lock_un)
|
||||
|
||||
def fileno(self) -> int:
|
||||
return self._fd
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
pass # Connections are shared and cached by speed.
|
||||
|
||||
@classmethod
|
||||
def _check_length(cls, length: int):
|
||||
if not 0 < length <= cls.MAX_TRANSFER_SIZE:
|
||||
raise ValueError(f"SPI transfer length must be between 1 and {cls.MAX_TRANSFER_SIZE}")
|
||||
|
||||
def xfer2(self, data) -> memoryview:
|
||||
length = len(data)
|
||||
if not 0 < length <= self.MAX_TRANSFER_SIZE:
|
||||
raise ValueError(f"SPI transfer length must be between 1 and {self.MAX_TRANSFER_SIZE}")
|
||||
self._view[:length] = bytes(data)
|
||||
self._transfer_words[4] = length # len field at byte offset 16
|
||||
if self._ioctl(self._fd, self.SPI_IOC_MESSAGE_1, self._transfer) != length:
|
||||
raise OSError("Short SPI transfer")
|
||||
return self._view[:length]
|
||||
|
||||
# Both operations use one SPI_IOC_MESSAGE(1) for the single-block calls in panda.
|
||||
def xfer(self, data) -> bytes:
|
||||
return bytes(self.xfer2(data))
|
||||
|
||||
def readbytes(self, length: int) -> bytes:
|
||||
self._check_length(length)
|
||||
data = os.read(self.fileno(), length)
|
||||
if len(data) != length:
|
||||
raise OSError("Short SPI read")
|
||||
return data
|
||||
|
||||
def writebytes(self, data):
|
||||
data = bytes(data)
|
||||
self._check_length(len(data))
|
||||
if os.write(self.fileno(), data) != len(data):
|
||||
raise OSError("Short SPI write")
|
||||
|
||||
class PandaSpiHandle(BaseHandle):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user