diff --git a/board/drivers/spi.h b/board/drivers/spi.h index ed88f6ea..d356c45c 100644 --- a/board/drivers/spi.h +++ b/board/drivers/spi.h @@ -78,7 +78,31 @@ void spi_rx_done(void) { 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 (spi_state == SPI_STATE_HEADER) { + if (memcmp(spi_buf_rx, "VERSION", 7) == 0) { + // protocol version request, respond with: + // VERSION + 2 byte data length + data + data complement + + // echo "VERSION" + (void)memcpy(spi_buf_tx, "VERSION", 7); + + // write response + uint16_t data_pos = 9; + uint16_t data_len = 1; + spi_buf_tx[data_pos] = 0x1; + + // response complement + for (uint16_t i = 0U; i < data_len; i++) { + spi_buf_tx[data_pos + data_len + i] = spi_buf_tx[data_pos + i] ^ 0xFFU; + } + + // data length + data_len *= 2U; + spi_buf_tx[7] = data_len & 0xFFU; + spi_buf_tx[8] = (data_len >> 8) & 0xFFU; + + response_len = 7U + 2U + data_len; + next_rx_state = SPI_STATE_HEADER_NACK;; + } else if (spi_state == SPI_STATE_HEADER) { checksum_valid = check_checksum(spi_buf_rx, SPI_HEADER_SIZE); if ((spi_buf_rx[0] == SPI_SYNC_BYTE) && checksum_valid) { // response: ACK and start receiving data portion diff --git a/python/spi.py b/python/spi.py index 22d257b2..ba1a3507 100644 --- a/python/spi.py +++ b/python/spi.py @@ -167,6 +167,44 @@ class PandaSpiHandle(BaseHandle): raise exc + def get_protocol_version(self) -> bytes: + vers_str = b"VERSION" + def _get_version(spi) -> bytes: + spi.writebytes(vers_str) + + logging.debug("- waiting for echo") + start = time.monotonic() + while True: + r = spi.readbytes(len(vers_str)) + if bytes(r) == vers_str: + break + if (time.monotonic() - start) > 0.5: + raise PandaSpiException("timed out waiting for version echo") + + # get response + logging.debug("- receiving response") + b = bytes(spi.readbytes(2)) + rlen = struct.unpack(" 1000: + raise PandaSpiException("response length greater than max") + + dat = bytes(spi.readbytes(rlen)) + resp = dat[:rlen//2] + resp_comp = dat[rlen//2:] + if resp != bytes([x ^ 0xff for x in resp_comp]): + raise PandaSpiException("data complement doesn't match") + return resp + + exc = PandaSpiException() + with self.dev.acquire() as spi: + for _ in range(10): + try: + return _get_version(spi) + except PandaSpiException as e: + exc = e + logging.debug("SPI get protocol version failed, retrying", exc_info=True) + raise exc + # libusb1 functions def close(self): self.dev.close() diff --git a/tests/hitl/8_spi.py b/tests/hitl/8_spi.py index c4a4502e..934630ff 100644 --- a/tests/hitl/8_spi.py +++ b/tests/hitl/8_spi.py @@ -17,6 +17,10 @@ class TestSpi: assert spy.call_count == 2 mocker.stop(spy) + def test_protocol_version(self, p): + v = p._handle.get_protocol_version() + assert v == b"\x01" + def test_all_comm_types(self, mocker, p): spy = mocker.spy(p._handle, '_wait_for_ack')