diff --git a/panda/board/drivers/drivers.h b/panda/board/drivers/drivers.h index a9cbfeb71..4b187a0e5 100644 --- a/panda/board/drivers/drivers.h +++ b/panda/board/drivers/drivers.h @@ -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 ******************** diff --git a/panda/board/drivers/spi.h b/panda/board/drivers/spi.h index 94cb645a7..ccbbac846 100644 --- a/panda/board/drivers/spi.h +++ b/panda/board/drivers/spi.h @@ -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) { diff --git a/panda/board/early_init.h b/panda/board/early_init.h index 9f1bc7c41..b5c366195 100644 --- a/panda/board/early_init.h +++ b/panda/board/early_init.h @@ -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(); } diff --git a/panda/board/main.c b/panda/board/main.c index 2625f4b6b..53a7195e9 100644 --- a/panda/board/main.c +++ b/panda/board/main.c @@ -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"); diff --git a/panda/board/main_comms.h b/panda/board/main_comms.h index 2c277d645..7d2cd4b57 100644 --- a/panda/board/main_comms.h +++ b/panda/board/main_comms.h @@ -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; diff --git a/panda/board/main_definitions.h b/panda/board/main_definitions.h index b07788beb..2c5ed5f8c 100644 --- a/panda/board/main_definitions.h +++ b/panda/board/main_definitions.h @@ -1,3 +1,5 @@ +#pragma once + #include "main_declarations.h" // ********************* Globals ********************** diff --git a/panda/board/stm32h7/llspi.h b/panda/board/stm32h7/llspi.h index ac3119eea..650c62cca 100644 --- a/panda/board/stm32h7/llspi.h +++ b/panda/board/stm32h7/llspi.h @@ -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); diff --git a/panda/board/stm32h7/sound.h b/panda/board/stm32h7/sound.h index f862351ca..d34b0c4ff 100644 --- a/panda/board/stm32h7/sound.h +++ b/panda/board/stm32h7/sound.h @@ -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 diff --git a/panda/board/sys/power_saving.h b/panda/board/sys/power_saving.h index 0b0e518a8..eb97d6281 100644 --- a/panda/board/sys/power_saving.h +++ b/panda/board/sys/power_saving.h @@ -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. diff --git a/panda/python/__init__.py b/panda/python/__init__.py index 754f1fb25..9d562fc1e 100644 --- a/panda/python/__init__.py +++ b/panda/python/__init__.py @@ -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'') diff --git a/panda/python/spi.py b/panda/python/spi.py index 311a8f32b..6c8758418 100644 --- a/panda/python/spi.py +++ b/panda/python/spi.py @@ -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(" 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): """