test: use pristine upstream panda SPI firmware (stage 2d)

This commit is contained in:
1okko
2026-10-20 21:37:32 +08:00
parent f426cf8ac8
commit c200eec550
11 changed files with 139 additions and 370 deletions
+3 -49
View File
@@ -25,7 +25,6 @@ typedef struct {
typedef struct {
uint8_t bus_lookup;
uint8_t can_num_lookup;
int8_t forwarding_bus;
uint32_t can_speed;
uint32_t can_data_speed;
bool canfd_auto;
@@ -71,11 +70,7 @@ extern bus_config_t bus_config[PANDA_CAN_CNT];
void can_init_all(void);
void can_set_orientation(bool flipped);
#ifdef PANDA_JUNGLE
void can_set_forwarding(uint8_t from, uint8_t to);
#endif
bool can_tx_check_min_slots_free(uint32_t min);
uint8_t calculate_checksum(const uint8_t *dat, uint32_t len);
void can_set_checksum(CANPacket_t *packet);
bool can_check_checksum(CANPacket_t *packet);
void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook);
@@ -92,7 +87,6 @@ struct fan_state_t {
uint16_t tach_counter;
uint16_t rpm;
uint8_t power;
float error_integral;
uint8_t cooldown_counter;
};
extern struct fan_state_t fan_state;
@@ -118,7 +112,6 @@ extern FDCAN_GlobalTypeDef *cans[PANDA_CAN_CNT];
void can_clear_send(FDCAN_GlobalTypeDef *FDCANx, uint8_t can_number);
void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg);
void can_rx(uint8_t can_number);
// ******************** harness ********************
@@ -157,7 +150,6 @@ void harness_init(void);
// ******************** interrupts ********************
typedef struct interrupt {
IRQn_Type irq_type;
void (*handler)(void);
uint32_t call_counter;
uint32_t call_rate;
@@ -167,12 +159,10 @@ typedef struct interrupt {
void interrupt_timer_init(void);
uint32_t microsecond_timer_get(void);
void unused_interrupt_handler(void);
extern interrupt interrupts[NUM_INTERRUPTS];
#define REGISTER_INTERRUPT(irq_num, func_ptr, call_rate_max, rate_fault) \
interrupts[irq_num].irq_type = (irq_num); \
interrupts[irq_num].handler = (func_ptr); \
interrupts[irq_num].call_counter = 0U; \
interrupts[irq_num].call_rate = 0U; \
@@ -232,48 +222,12 @@ extern uint16_t spi_error_count;
void can_tx_comms_resume_spi(void);
void spi_init(void);
// ******************** uart ********************
#ifdef STM32H7
// ***************************** Definitions *****************************
#define FIFO_SIZE_INT 0x400U
typedef struct uart_ring {
volatile uint16_t w_ptr_tx;
volatile uint16_t r_ptr_tx;
uint8_t *elems_tx;
uint32_t tx_fifo_size;
volatile uint16_t w_ptr_rx;
volatile uint16_t r_ptr_rx;
uint8_t *elems_rx;
uint32_t rx_fifo_size;
USART_TypeDef *uart;
void (*callback)(struct uart_ring*);
bool overwrite;
} uart_ring;
// ***************************** Function prototypes *****************************
void debug_ring_callback(uart_ring *ring);
void uart_tx_ring(uart_ring *q);
uart_ring *get_ring_by_number(int a);
// ************************* Low-level buffer functions *************************
bool get_char(uart_ring *q, char *elem);
bool injectc(uart_ring *q, char elem);
bool put_char(uart_ring *q, char elem);
void clear_uart_buff(uart_ring *q);
// ************************ High-level debug functions **********************
void putch(const char a);
// ******************** debug ********************
bool debug_get_char(char *elem);
void print(const char *a);
void puthx(uint32_t i, uint8_t len);
void puth(unsigned int i);
#if defined(DEBUG_SPI) || defined(BOOTSTUB) || defined(DEBUG)
static void puth4(unsigned int i);
#endif
#if defined(DEBUG_SPI) || defined(DEBUG_USB) || defined(DEBUG_COMMS)
static void hexdump(const void *a, int l);
#endif
#endif // STM32H7
static inline void puth4(unsigned int i);
// ******************** usb ********************
+3 -162
View File
@@ -188,168 +188,9 @@ void spi_init(void) {
crc32_init();
llspi_init();
// Start the first packet!
spi_state = SPI_STATE_HEADER;
llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE);
}
static bool validate_checksum(const uint8_t *data, uint16_t len) {
// TODO: can speed this up by casting the bulk to uint32_t and xor-ing the bytes afterwards
uint8_t checksum = SPI_CHECKSUM_START;
for(uint16_t i = 0U; i < len; i++){
checksum ^= data[i];
}
return checksum == 0U;
}
void spi_rx_done(void) {
uint16_t response_len = 0U;
uint8_t next_rx_state = SPI_STATE_HEADER_NACK;
bool checksum_valid = false;
static uint8_t spi_endpoint;
static uint16_t spi_data_len_miso;
// parse header
spi_endpoint = spi_buf_rx[1];
spi_data_len_mosi = (spi_buf_rx[3] << 8) | spi_buf_rx[2];
spi_data_len_miso = (spi_buf_rx[5] << 8) | spi_buf_rx[4];
if (memcmp(spi_buf_rx, version_text, 7) == 0) {
response_len = spi_version_packet(spi_buf_tx);
next_rx_state = SPI_STATE_HEADER_NACK;;
} else if (spi_state == SPI_STATE_HEADER) {
checksum_valid = validate_checksum(spi_buf_rx, SPI_HEADER_SIZE);
if ((spi_buf_rx[0] == SPI_SYNC_BYTE) && checksum_valid) {
// response: ACK and start receiving data portion
spi_buf_tx[0] = SPI_HACK;
next_rx_state = SPI_STATE_HEADER_ACK;
response_len = 1U;
} else {
// response: NACK and reset state machine
#ifdef DEBUG_SPI
print("- incorrect header sync or checksum "); hexdump(spi_buf_rx, SPI_HEADER_SIZE);
#endif
spi_buf_tx[0] = SPI_NACK;
next_rx_state = SPI_STATE_HEADER_NACK;
response_len = 1U;
}
} else if (spi_state == SPI_STATE_DATA_RX) {
// We got everything! Based on the endpoint specified, call the appropriate handler
bool response_ack = false;
checksum_valid = validate_checksum(&(spi_buf_rx[SPI_HEADER_SIZE]), spi_data_len_mosi + 1U);
if (checksum_valid) {
if (spi_endpoint == 0U) {
if (spi_data_len_mosi >= sizeof(ControlPacket_t)) {
ControlPacket_t ctrl = {0};
(void)memcpy((uint8_t*)&ctrl, &spi_buf_rx[SPI_HEADER_SIZE], sizeof(ControlPacket_t));
response_len = comms_control_handler(&ctrl, &spi_buf_tx[3]);
response_ack = true;
} else {
print("SPI: insufficient data for control handler\n");
}
} else if ((spi_endpoint == 1U) || (spi_endpoint == 0x81U)) {
if (spi_data_len_mosi == 0U) {
response_len = comms_can_read(&(spi_buf_tx[3]), spi_data_len_miso);
response_ack = true;
} else {
print("SPI: did not expect data for can_read\n");
}
} else if (spi_endpoint == 2U) {
comms_endpoint2_write(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi);
response_ack = true;
} else if (spi_endpoint == 3U) {
if (spi_data_len_mosi > 0U) {
if (spi_can_tx_ready) {
spi_can_tx_ready = false;
comms_can_write(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi);
response_ack = true;
} else {
response_ack = false;
print("SPI: CAN NACK\n");
}
} else {
print("SPI: did expect data for can_write\n");
}
} else if (spi_endpoint == 0xABU) {
// test endpoint: mimics panda -> device transfer
response_len = spi_data_len_miso;
response_ack = true;
} else if (spi_endpoint == 0xACU) {
// test endpoint: mimics device -> panda transfer (with NACK)
response_ack = false;
} else {
print("SPI: unexpected endpoint"); puth(spi_endpoint); print("\n");
}
} else {
// Checksum was incorrect
response_ack = false;
#ifdef DEBUG_SPI
print("- incorrect data checksum ");
puth4(spi_data_len_mosi);
print("\n");
hexdump(spi_buf_rx, SPI_HEADER_SIZE);
hexdump(&(spi_buf_rx[SPI_HEADER_SIZE]), MIN(spi_data_len_mosi, 64));
print("\n");
#endif
}
if (!response_ack) {
spi_buf_tx[0] = SPI_NACK;
next_rx_state = SPI_STATE_HEADER_NACK;
response_len = 1U;
} else {
// Setup response header
spi_buf_tx[0] = SPI_DACK;
spi_buf_tx[1] = response_len & 0xFFU;
spi_buf_tx[2] = (response_len >> 8) & 0xFFU;
// Add checksum
uint8_t checksum = SPI_CHECKSUM_START;
for(uint16_t i = 0U; i < (response_len + 3U); i++) {
checksum ^= spi_buf_tx[i];
}
spi_buf_tx[response_len + 3U] = checksum;
response_len += 4U;
next_rx_state = SPI_STATE_DATA_TX;
}
} else {
print("SPI: RX unexpected state: "); puth(spi_state); print("\n");
}
// send out response
if (response_len == 0U) {
print("SPI: no response\n");
spi_buf_tx[0] = SPI_NACK;
spi_state = SPI_STATE_HEADER_NACK;
response_len = 1U;
}
llspi_miso_dma(spi_buf_tx, response_len);
spi_state = next_rx_state;
if (!checksum_valid) {
spi_error_count += 1U;
}
}
void spi_tx_done(bool reset) {
if ((spi_state == SPI_STATE_HEADER_NACK) || reset) {
// Reset state
spi_state = SPI_STATE_HEADER;
llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE);
} else if (spi_state == SPI_STATE_HEADER_ACK) {
// ACK was sent, queue up the RX buf for the data + checksum
spi_state = SPI_STATE_DATA_RX;
llspi_mosi_dma(&spi_buf_rx[SPI_HEADER_SIZE], spi_data_len_mosi + 1U);
} else if (spi_state == SPI_STATE_DATA_TX) {
// Reset state
spi_state = SPI_STATE_HEADER;
llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE);
} else {
spi_state = SPI_STATE_HEADER;
llspi_mosi_dma(spi_buf_rx, SPI_HEADER_SIZE);
print("SPI: TX unexpected state: "); puth(spi_state); print("\n");
}
// kick off the first read
spi_buf_ready[0] = SPI_SLAVE_READY;
llspi_queue(spi_buf_ready, 1U);
}
void can_tx_comms_resume_spi(void) {
+2 -1
View File
@@ -1,3 +1,5 @@
#pragma once
// Early bringup
#define ENTER_BOOTLOADER_MAGIC 0xdeadbeefU
#define ENTER_SOFTLOADER_MAGIC 0xdeadc0deU
@@ -57,7 +59,6 @@ void early_initialization(void) {
if (enter_bootloader_mode == ENTER_BOOTLOADER_MAGIC) {
led_init();
current_board->init_bootloader();
led_set(LED_GREEN, 1);
jump_to_bootloader();
}
+19 -53
View File
@@ -25,16 +25,6 @@
#include "board/can_comms.h"
#include "board/main_comms.h"
// ********************* Serial debugging *********************
void debug_ring_callback(uart_ring *ring) {
char rcv;
while (get_char(ring, &rcv)) {
(void)put_char(ring, rcv); // misra-c2012-17.7: cast to void is ok: debug function
}
}
// ****************************** safety mode ******************************
// this is the only way to leave silent mode
@@ -114,13 +104,12 @@ static void __attribute__ ((noinline)) enable_fpu(void) {
// called at 8Hz
static void tick_handler(void) {
static uint32_t siren_countdown = 0; // siren plays while countdown > 0
static uint32_t controls_allowed_countdown = 0;
static uint8_t prev_harness_status = HARNESS_STATUS_NC;
static uint8_t loop_counter = 0U;
static bool relay_malfunction_prev = false;
if (TICK_TIMER->SR != 0U) {
static uint32_t siren_countdown = 0; // siren plays while countdown > 0
static uint32_t controls_allowed_countdown = 0;
static uint8_t prev_harness_status = HARNESS_STATUS_NC;
static uint8_t loop_counter = 0U;
static bool relay_malfunction_prev = false;
heartbeat_tick_counter += 1U;
@@ -160,15 +149,6 @@ static void tick_handler(void) {
// decimated to 1Hz
if (loop_counter == 0U) {
//puth(usart1_dma); print(" "); puth(DMA2_Stream5->M0AR); print(" "); puth(DMA2_Stream5->NDTR); print("\n");
#ifdef DEBUG
print("** blink ");
print("rx:"); puth4(can_rx_q.r_ptr); print("-"); puth4(can_rx_q.w_ptr); print(" ");
print("tx1:"); puth4(can_tx1_q.r_ptr); print("-"); puth4(can_tx1_q.w_ptr); print(" ");
print("tx2:"); puth4(can_tx2_q.r_ptr); print("-"); puth4(can_tx2_q.w_ptr); print(" ");
print("tx3:"); puth4(can_tx3_q.r_ptr); print("-"); puth4(can_tx3_q.w_ptr); print("\n");
#endif
// set green LED to be controls allowed
led_set(LED_GREEN, controls_allowed);
@@ -302,6 +282,7 @@ int main(void) {
print("Config:\n");
print(" Board type: 0x"); puth(hw_type); print("\n");
print(" MCU UID: "); hexdump((const uint8_t *)UID_BASE, 12);
// init board
current_board->init();
@@ -331,9 +312,6 @@ int main(void) {
REGISTER_INTERRUPT(TICK_TIMER_IRQ, tick_handler, 10U, FAULT_INTERRUPT_RATE_TICK)
tick_timer_init();
#ifdef DEBUG
print("DEBUG ENABLED\n");
#endif
// enable USB (right before interrupts or enum can fail!)
usb_init();
@@ -365,32 +343,20 @@ int main(void) {
}
#endif
if (!power_save_enabled) {
#ifdef DEBUG_FAULTS
if (fault_status == FAULT_STATUS_NONE) {
#endif
// useful for debugging, fade breaks = panda is overloaded
for (uint32_t fade = 0U; fade < MAX_LED_FADE; fade += 1U) {
led_set(LED_RED, true);
delay(fade >> 4);
led_set(LED_RED, false);
delay((MAX_LED_FADE - fade) >> 4);
}
// useful for debugging, fade breaks = panda is overloaded
for (uint32_t fade = 0U; fade < MAX_LED_FADE; fade += 1U) {
led_set(LED_RED, true);
delay(fade >> 4);
led_set(LED_RED, false);
delay((MAX_LED_FADE - fade) >> 4);
}
for (uint32_t fade = MAX_LED_FADE; fade > 0U; fade -= 1U) {
led_set(LED_RED, true);
delay(fade >> 4);
led_set(LED_RED, false);
delay((MAX_LED_FADE - fade) >> 4);
}
#ifdef DEBUG_FAULTS
} else {
led_set(LED_RED, 1);
delay(512000U);
led_set(LED_RED, 0);
delay(512000U);
}
#endif
for (uint32_t fade = MAX_LED_FADE; fade > 0U; fade -= 1U) {
led_set(LED_RED, true);
delay(fade >> 4);
led_set(LED_RED, false);
delay((MAX_LED_FADE - fade) >> 4);
}
} else {
if ((hw_type == HW_TYPE_CUATRO) && !current_board->read_som_gpio()) {
assert_fatal(current_safety_mode == SAFETY_SILENT, "Error: Entering low power mode while not in SAFETY_SILENT. Hanging\n");
+11 -33
View File
@@ -1,3 +1,5 @@
#pragma once
extern int _app_start[0xc000]; // Only first 3 sectors of size 0x4000 are used
// Prototypes
@@ -54,32 +56,16 @@ static int get_health_pkt(void *dat) {
return sizeof(*health);
}
// send on serial, first byte to select the ring
// Endpoint 2 is only used by the bootstub for flashing.
void comms_endpoint2_write(const uint8_t *data, uint32_t len) {
uart_ring *ur = get_ring_by_number(data[0]);
if ((len != 0U) && (ur != NULL)) {
if ((data[0] < 2U) || (data[0] >= 4U)) {
for (uint32_t i = 1; i < len; i++) {
while (!put_char(ur, data[i])) {
// wait
}
}
}
}
UNUSED(data);
UNUSED(len);
}
int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
unsigned int resp_len = 0;
uart_ring *ur = NULL;
uint32_t time;
#ifdef DEBUG_COMMS
print("raw control request: "); hexdump(req, sizeof(ControlPacket_t)); print("\n");
print("- request "); puth(req->request); print("\n");
print("- param1 "); puth(req->param1); print("\n");
print("- param2 "); puth(req->param2); print("\n");
#endif
switch (req->request) {
// **** 0xa8: get microsecond timer
case 0xa8:
@@ -112,6 +98,12 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
stop_mode_requested = true;
break;
#endif
// **** 0xb6: read debug logs
case 0xb6:
while ((resp_len < req->length) && (resp_len < USBPACKET_MAX_SIZE) && debug_get_char((char*)&resp[resp_len])) {
++resp_len;
}
break;
// **** 0xc0: reset communications state
case 0xc0:
comms_can_reset();
@@ -257,20 +249,6 @@ int comms_control_handler(ControlPacket_t *req, uint8_t *resp) {
mads_set_alternative_experience(&alternative_experience);
}
break;
// **** 0xe0: uart read
case 0xe0:
ur = get_ring_by_number(req->param1);
if (!ur) {
break;
}
// read
uint16_t req_length = MIN(req->length, USBPACKET_MAX_SIZE);
while ((resp_len < req_length) &&
get_char(ur, (char*)&resp[resp_len])) {
++resp_len;
}
break;
// **** 0xe5: set CAN loopback (for testing)
case 0xe5:
can_loopback = req->param1 > 0U;
+2
View File
@@ -1,3 +1,5 @@
#pragma once
#include "main_declarations.h"
// ********************* Globals **********************
+4 -7
View File
@@ -1,10 +1,7 @@
// master -> panda DMA start
// cppcheck-suppress constParameterPointer ; RX DMA writes through addr after return
void llspi_mosi_dma(uint8_t *addr, int len) {
// disable DMA + SPI
register_clear_bits(&(SPI4->CFG1), SPI_CFG1_RXDMAEN);
DMA2_Stream2->CR &= ~DMA_SxCR_EN;
register_clear_bits(&(SPI4->CR1), SPI_CR1_SPE);
#pragma once
// SPI4 NSS (chip select) is PE11
#define SPI_CS_PIN 11U
void spi_cs_rising(void);
+2
View File
@@ -1,3 +1,5 @@
#pragma once
#define SOUND_RX_BUF_SIZE 1000U
#define SOUND_TX_BUF_SIZE (SOUND_RX_BUF_SIZE/2U)
#define MIC_RX_BUF_SIZE 512U
+2
View File
@@ -1,3 +1,5 @@
#pragma once
#include "board/sys/sys.h"
// WARNING: To stay in compliance with the SIL2 rules laid out in STM UM2331, we should never use any of the available hardware low power modes during safety function execution.
+8 -29
View File
@@ -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
View File
@@ -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):
"""