#pragma once #include "spi_declarations.h" #include "crc.h" #ifdef STM32H7 #define SPI_BUF_SIZE 2048U // H7 DMA2 located in D2 domain, so we need to use SRAM1/SRAM2 __attribute__((section(".sram12"))) uint8_t spi_buf_rx[SPI_BUF_SIZE]; __attribute__((section(".sram12"))) uint8_t spi_buf_tx[SPI_BUF_SIZE]; #else #define SPI_BUF_SIZE 1024U uint8_t spi_buf_rx[SPI_BUF_SIZE]; uint8_t spi_buf_tx[SPI_BUF_SIZE]; #endif uint16_t spi_checksum_error_count = 0; #if defined(ENABLE_SPI) || defined(BOOTSTUB) static uint8_t spi_state = SPI_STATE_HEADER; static uint16_t spi_data_len_mosi; static bool spi_can_tx_ready = false; static const unsigned char version_text[] = "VERSION"; static uint16_t spi_version_packet(uint8_t *out) { // this protocol version request is a stable portion of // the panda's SPI protocol. its contents match that of the // panda USB descriptors and are sufficent to list/enumerate // a panda, determine panda type, and bootstub status. // the response is: // VERSION + 2 byte data length + data + CRC8 // echo "VERSION" (void)memcpy(out, version_text, 7); // write response uint16_t data_len = 0; uint16_t data_pos = 7U + 2U; // write serial (void)memcpy(&out[data_pos], ((uint8_t *)UID_BASE), 12); data_len += 12U; // HW type out[data_pos + data_len] = hw_type; data_len += 1U; // bootstub out[data_pos + data_len] = USB_PID & 0xFFU; data_len += 1U; // SPI protocol version out[data_pos + data_len] = 0x2; data_len += 1U; // data length out[7] = data_len & 0xFFU; out[8] = (data_len >> 8) & 0xFFU; // CRC8 uint16_t resp_len = data_pos + data_len; out[resp_len] = crc_checksum(out, resp_len, 0xD5U); resp_len += 1U; return resp_len; } void spi_init(void) { // platform 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 print("- incorrect header sync or checksum "); hexdump(spi_buf_rx, SPI_HEADER_SIZE); 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, send max response length response_len = spi_data_len_miso; response_ack = true; } else { print("SPI: unexpected endpoint"); puth(spi_endpoint); print("\n"); } } else { // Checksum was incorrect response_ack = false; 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"); } 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_checksum_error_count < UINT16_MAX)) { spi_checksum_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"); } } void can_tx_comms_resume_spi(void) { spi_can_tx_ready = true; } #else void can_tx_comms_resume_spi(void) { return; } #endif