From ae26b75d7bac3d57b060f9fdc48b591fa23e0a25 Mon Sep 17 00:00:00 2001 From: Igor Biletskyy Date: Fri, 12 Nov 2021 16:36:34 -0800 Subject: [PATCH] CAN_FIFOMailBox to CANPacket struct + USB dynamic packet size (#739) * Squashed commits, no cleanup * Few fixes * No init = garbage * Only receive with new canpacket * Add send with canpacket * Revert "Add send with canpacket" This reverts commit 7d06686ddd6d447c714b5289d31af24403d36931. * Packet must be aligned to word, or bad performance * Cleaner * Fix tests * Tests... * MISRA 10.4 * More MISRA * libpandasafety_py * cffi * even more tests... * typo * ... * ... * ... * Slight cleanup * MISRA 6.1 * MISRA 17.7 * Bug in bxcan + even style * MISRA 10.1 * Revert "MISRA 10.1" This reverts commit 404ae7fcc39556f80f528de9015702e69f4ea0a5. * ... * MISRA 10.1 and 10.4 suppress until next PR * MISRA 20.1 * ... * test_honda * ... * ... * test_toyota * test_volkswagen_mqb * test_volkswagen_pq * Sketchy thing... * Revert "Sketchy thing..." This reverts commit 3b2e5715bdc1954f7b7b3b7469ba3d0eaa06bdf9. * remove comment * bxcan extended address bug * Concept, experimental dynamic usb packet size * increase each buffer to 10240 bytes * raise python bulk read/write limits * ... * Move packet size to start * Experimental send, stream-like * New receive test, stream-like * cleanup * cleanup + rebase fixes * MISRA * Extra receive method, stream-like, commented out * type change * Revert back to buffer for send, stream commented * forgot ZLP * lower buffer, add rx failsafe * ... remove ZLP * return ZLP back * Add tx checks to panda fw * TX stream with counter * fix counter overflow * 13 free slots should be enough * limit tx usb packet * ... * Revert max_bulk_msg doubling * python lib improve speed * Stream with counter for RX, dirty, needs cleanup * Increase chunk length to 4096 bytes * cleanup fdcan.h * cleanup __init__.py * MISRA 12.1 * MISRA 10.8 * remove non-streaming usb functions * more main.c cleanup * MISRA 15.6 * MISRA 15.5 * MISRA 18.4 and suppress objectIndex * handling usb pakcets > 63bytes, naming and cleanup * Cleanup old from tests and update CANPacket_t struct * Switch to 4 bit DLC instead of 6 bit length * ops) * ... * pylint * receive python buffer increase * USB increase receive packet len * tweak buffers * No need for so high limits * MISRA 20.1 workaround * performance tweaks * cleanup, dlc to data_len_code naming * main.c naming * comments and cleanup for main.c usb * clean py lib * pylint * do not discard good rx messages on stream fail * cleanups * naming * remove bitstruct lib and lower tx limit * bitstruct lefovers * fix bug in VW test * remove adjusting data size and assert on wrong len * ... * test new memcpy before merging * Revert "test new memcpy before merging" This reverts commit 399465a264835061adabdd785718c4b6fc18c267. * macros for to/fromuint8_t array * MISRA hates me! * tests.c include macros instead * move CANPacket to can_definitions.h * vw_pq python test fix * new memcpy test, REMOVE * check without alignment * revert macros for uint8 arrays * Revert "revert macros for uint8 arrays" This reverts commit 581a9db735a42d0d68200bd270d87a8fd34e43fe. * check assert * Revert "check assert" This reverts commit 9e970d029a50597a1718b2bb0260196c050fd77f. * one more variation * Revert "one more variation" This reverts commit f6c0528b7ac7e125750dc0d9445c7ce97f6954b5. * what about read performance * Revert "what about read performance" This reverts commit d2610f90958a816fe7f1822157a84f85e97d9249. * check struct alignment to word * check for aligned memcpy again * cleanup * add CANPacket structure diagram * update CANPacket and add USB packet struct * bugfix + refactoring of EP1 * move dlc_to_len to header * missed include * typo... * MISRA * fk * lower MAX_CAN_MSGS_PER_BULK_TRANSFER * bump CAN_PACKET_VERSION to 2 * bump python lib CAN_PACKET_VERSION to 2 * rename parse_can_buffer to unpack_can_buffer * CANPacket_t const fields * Revert "CANPacket_t const fields" This reverts commit cf91c035b7706a14e317550c5f0501ae3fce7c70. * test.c relative path * cleanup * move macros to safety_declarations * Refactor pack/unpack funcs and add unittest * usb_protocol.h * oops * Update .github/workflows/test.yaml Co-authored-by: Adeeb Shihadeh * remove print from unittest Co-authored-by: Adeeb Shihadeh --- .github/workflows/test.yaml | 12 +++ CANPacket_structure.png | Bin 0 -> 39535 bytes USB_packet_structure.png | Bin 0 -> 21782 bytes __init__.py | 4 +- board/can_definitions.h | 29 +++++++ board/config.h | 12 --- board/dlc_to_len.h | 1 + board/drivers/bxcan.h | 57 ++++++++----- board/drivers/can_common.h | 30 +++---- board/drivers/fdcan.h | 78 +++++++++++------- board/drivers/gmlan_alt.h | 17 ++-- board/drivers/usb.h | 7 +- board/flasher.h | 2 +- board/main.c | 32 +------- board/pedal/main.c | 8 +- board/safety.h | 18 ++-- board/safety/safety_chrysler.h | 12 +-- board/safety/safety_defaults.h | 8 +- board/safety/safety_elm327.h | 2 +- board/safety/safety_ford.h | 4 +- board/safety/safety_gm.h | 4 +- board/safety/safety_honda.h | 14 ++-- board/safety/safety_hyundai.h | 12 +-- board/safety/safety_mazda.h | 6 +- board/safety/safety_nissan.h | 6 +- board/safety/safety_subaru.h | 18 ++-- board/safety/safety_tesla.h | 6 +- board/safety/safety_toyota.h | 11 +-- board/safety/safety_volkswagen.h | 20 ++--- board/safety_declarations.h | 27 +++--- board/stm32fx/stm32fx_config.h | 4 + board/stm32h7/llfdcan.h | 7 -- board/stm32h7/stm32h7_config.h | 4 + board/usb_protocol.h | 108 ++++++++++++++++++++++++ python/__init__.py | 123 +++++++++++++++++++--------- tests/gmbitbang/test_packer.c | 16 ++-- tests/misra/suppressions.txt | 6 +- tests/safety/common.py | 18 ++-- tests/safety/libpandasafety_py.py | 33 ++++---- tests/safety/test.c | 26 +----- tests/safety/test_honda.py | 11 ++- tests/safety/test_toyota.py | 11 ++- tests/safety/test_volkswagen_mqb.py | 2 +- tests/safety/test_volkswagen_pq.py | 4 +- tests/safety_replay/helpers.py | 19 ++--- tests/usbprotocol/test_pandalib.py | 23 ++++++ 46 files changed, 531 insertions(+), 341 deletions(-) create mode 100644 CANPacket_structure.png create mode 100644 USB_packet_structure.png create mode 100644 board/can_definitions.h create mode 100644 board/dlc_to_len.h create mode 100644 board/usb_protocol.h create mode 100644 tests/usbprotocol/test_pandalib.py diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index e96c5f5c..442616cb 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -41,6 +41,18 @@ jobs: - name: Build pedal STM image and bootstub run: $RUN "cd /tmp/openpilot/panda && PEDAL=1 scons" + unit_tests: + name: unit tests + runs-on: ubuntu-20.04 + timeout-minutes: 45 + steps: + - uses: actions/checkout@v2 + - name: Build Docker image + run: eval "$BUILD" + - name: Test pack/unpack for USB protocol + run: $RUN "cd /tmp/openpilot/panda/tests/usbprotocol && + python -m unittest discover ." + safety: name: safety runs-on: ubuntu-20.04 diff --git a/CANPacket_structure.png b/CANPacket_structure.png new file mode 100644 index 0000000000000000000000000000000000000000..7dd134316112bef9688d261cf33d3a78aa63be68 GIT binary patch literal 39535 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 Ny=8E-Ldz!X{{XZ5{Eh$s literal 0 HcmV?d00001 diff --git a/USB_packet_structure.png b/USB_packet_structure.png new file mode 100644 index 0000000000000000000000000000000000000000..30e848c3f6a84494abb6f7a1f7169d06dc0aa6d8 GIT binary patch literal 21782 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 literal 0 HcmV?d00001 diff --git a/__init__.py b/__init__.py index 81ee2f01..a9bf38f4 100644 --- a/__init__.py +++ b/__init__.py @@ -1,8 +1,8 @@ # flake8: noqa # pylint: skip-file from .python import Panda, PandaWifiStreaming, PandaDFU, flash_release, \ - BASEDIR, ensure_st_up_to_date, PandaSerial, \ - DEFAULT_FW_FN, DEFAULT_H7_FW_FN, MCU_TYPE_H7, MCU_TYPE_F4 + BASEDIR, ensure_st_up_to_date, PandaSerial, pack_can_buffer, unpack_can_buffer, \ + DEFAULT_FW_FN, DEFAULT_H7_FW_FN, MCU_TYPE_H7, MCU_TYPE_F4, DLC_TO_LEN, LEN_TO_DLC from .python.config import BOOTSTUB_ADDRESS, BLOCK_SIZE_FX, APP_ADDRESS_FX, \ BLOCK_SIZE_H7, APP_ADDRESS_H7, DEVICE_SERIAL_NUMBER_ADDR_H7, \ diff --git a/board/can_definitions.h b/board/can_definitions.h new file mode 100644 index 00000000..552cf3ff --- /dev/null +++ b/board/can_definitions.h @@ -0,0 +1,29 @@ +#include "dlc_to_len.h" + +#define CAN_PACKET_VERSION 2 +typedef struct { + unsigned char reserved : 1; + unsigned char bus : 3; + unsigned char data_len_code : 4; + unsigned char rejected : 1; + unsigned char returned : 1; + unsigned char extended : 1; + unsigned int addr : 29; + unsigned char data[CANPACKET_DATA_SIZE_MAX]; +} __attribute__((packed, aligned(4))) CANPacket_t; + +#define GET_BUS(msg) ((msg)->bus) +#define GET_LEN(msg) (dlc_to_len[(msg)->data_len_code]) +#define GET_ADDR(msg) ((msg)->addr) + +// Flasher and pedal use raw mailbox access +#define GET_MAILBOX_BYTE(msg, b) (((int)(b) > 3) ? (((msg)->RDHR >> (8U * ((unsigned int)(b) % 4U))) & 0xFFU) : (((msg)->RDLR >> (8U * (unsigned int)(b))) & 0xFFU)) +#define GET_MAILBOX_BYTES_04(msg) ((msg)->RDLR) +#define GET_MAILBOX_BYTES_48(msg) ((msg)->RDHR) + +#define CAN_INIT_TIMEOUT_MS 500U + +#define CANPACKET_HEAD_SIZE 5U + +#define WORD_TO_BYTE_ARRAY(dst8, src32) 0[dst8] = ((src32) & 0xFF); 1[dst8] = (((src32) >> 8) & 0xFF); 2[dst8] = (((src32) >> 16) & 0xFF); 3[dst8] = (((src32) >> 24) & 0xFF) +#define BYTE_ARRAY_TO_WORD(dst32, src8) ((dst32) = 0[src8] | (1[src8] << 8) | (2[src8] << 16) | (3[src8] << 24)) diff --git a/board/config.h b/board/config.h index c7a50e70..311a0a2e 100644 --- a/board/config.h +++ b/board/config.h @@ -34,18 +34,6 @@ #define MAX_RESP_LEN 0x40U -#define CAN_PACKET_VERSION 1 - -#define GET_BUS(msg) (((msg)->RDTR >> 4) & 0xFF) -#define GET_LEN(msg) ((msg)->RDTR & 0xF) -#define GET_ADDR(msg) ((((msg)->RIR & 4) != 0) ? ((msg)->RIR >> 3) : ((msg)->RIR >> 21)) -#define GET_BYTE(msg, b) (((int)(b) > 3) ? (((msg)->RDHR >> (8U * ((unsigned int)(b) % 4U))) & 0xFFU) : (((msg)->RDLR >> (8U * (unsigned int)(b))) & 0xFFU)) -#define GET_BYTES_04(msg) ((msg)->RDLR) -#define GET_BYTES_48(msg) ((msg)->RDHR) -#define GET_FLAG(value, mask) (((__typeof__(mask))(value) & (mask)) == (mask)) - -#define CAN_INIT_TIMEOUT_MS 500U - #include #ifdef STM32H7 #include "stm32h7/stm32h7_config.h" diff --git a/board/dlc_to_len.h b/board/dlc_to_len.h new file mode 100644 index 00000000..82d743ff --- /dev/null +++ b/board/dlc_to_len.h @@ -0,0 +1 @@ +unsigned char dlc_to_len[] = {0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 12U, 16U, 20U, 24U, 32U, 48U, 64U}; diff --git a/board/drivers/bxcan.h b/board/drivers/bxcan.h index e704b2f4..10cee05f 100644 --- a/board/drivers/bxcan.h +++ b/board/drivers/bxcan.h @@ -101,18 +101,23 @@ void process_can(uint8_t can_number) { uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number); // check for empty mailbox - CAN_FIFOMailBox_TypeDef to_send; + CANPacket_t to_send; if ((CAN->TSR & CAN_TSR_TME0) == CAN_TSR_TME0) { // add successfully transmitted message to my fifo if ((CAN->TSR & CAN_TSR_RQCP0) == CAN_TSR_RQCP0) { can_txd_cnt += 1; if ((CAN->TSR & CAN_TSR_TXOK0) == CAN_TSR_TXOK0) { - CAN_FIFOMailBox_TypeDef to_push; - to_push.RIR = CAN->sTxMailBox[0].TIR; - to_push.RDTR = (CAN->sTxMailBox[0].TDTR & 0xFFFF000FU) | ((CAN_BUS_RET_FLAG | bus_number) << 4); - to_push.RDLR = CAN->sTxMailBox[0].TDLR; - to_push.RDHR = CAN->sTxMailBox[0].TDHR; + CANPacket_t to_push; + to_push.returned = 1U; + to_push.rejected = 0U; + to_push.extended = (CAN->sTxMailBox[0].TIR >> 2) & 0x1U; + to_push.addr = (to_push.extended != 0) ? (CAN->sTxMailBox[0].TIR >> 3) : (CAN->sTxMailBox[0].TIR >> 21); + to_push.data_len_code = CAN->sTxMailBox[0].TDTR & 0xFU; + to_push.bus = bus_number; + WORD_TO_BYTE_ARRAY(&to_push.data[0], CAN->sTxMailBox[0].TDLR); + WORD_TO_BYTE_ARRAY(&to_push.data[4], CAN->sTxMailBox[0].TDHR); + can_send_errs += can_push(&can_rx_q, &to_push) ? 0U : 1U; } @@ -136,10 +141,12 @@ void process_can(uint8_t can_number) { if (can_pop(can_queues[bus_number], &to_send)) { can_tx_cnt += 1; // only send if we have received a packet - CAN->sTxMailBox[0].TDLR = to_send.RDLR; - CAN->sTxMailBox[0].TDHR = to_send.RDHR; - CAN->sTxMailBox[0].TDTR = to_send.RDTR; - CAN->sTxMailBox[0].TIR = to_send.RIR; + CAN->sTxMailBox[0].TIR = ((to_send.extended != 0) ? (to_send.addr << 3) : (to_send.addr << 21)) | (to_send.extended << 2); + CAN->sTxMailBox[0].TDTR = to_send.data_len_code; + BYTE_ARRAY_TO_WORD(CAN->sTxMailBox[0].TDLR, &to_send.data[0]); + BYTE_ARRAY_TO_WORD(CAN->sTxMailBox[0].TDHR, &to_send.data[4]); + // Send request TXRQ + CAN->sTxMailBox[0].TIR |= 0x1U; usb_cb_ep3_out_complete(); } @@ -161,23 +168,29 @@ void can_rx(uint8_t can_number) { pending_can_live = 1; // add to my fifo - CAN_FIFOMailBox_TypeDef to_push; - to_push.RIR = CAN->sFIFOMailBox[0].RIR; - to_push.RDTR = CAN->sFIFOMailBox[0].RDTR; - to_push.RDLR = CAN->sFIFOMailBox[0].RDLR; - to_push.RDHR = CAN->sFIFOMailBox[0].RDHR; + CANPacket_t to_push; - // modify RDTR for our API - to_push.RDTR = (to_push.RDTR & 0xFFFF000F) | (bus_number << 4); + to_push.returned = 0U; + to_push.rejected = 0U; + to_push.extended = (CAN->sFIFOMailBox[0].RIR >> 2) & 0x1U; + to_push.addr = (to_push.extended != 0) ? (CAN->sFIFOMailBox[0].RIR >> 3) : (CAN->sFIFOMailBox[0].RIR >> 21); + to_push.data_len_code = CAN->sFIFOMailBox[0].RDTR & 0xFU; + to_push.bus = bus_number; + WORD_TO_BYTE_ARRAY(&to_push.data[0], CAN->sFIFOMailBox[0].RDLR); + WORD_TO_BYTE_ARRAY(&to_push.data[4], CAN->sFIFOMailBox[0].RDHR); // forwarding (panda only) int bus_fwd_num = safety_fwd_hook(bus_number, &to_push); if (bus_fwd_num != -1) { - CAN_FIFOMailBox_TypeDef to_send; - to_send.RIR = to_push.RIR | 1; // TXRQ - to_send.RDTR = to_push.RDTR; - to_send.RDLR = to_push.RDLR; - to_send.RDHR = to_push.RDHR; + CANPacket_t to_send; + + to_send.returned = 0U; + to_push.rejected = 0U; + to_send.extended = to_push.extended; // TXRQ + to_send.addr = to_push.addr; + to_send.bus = to_push.bus; + to_send.data_len_code = to_push.data_len_code; + (void)memcpy(to_send.data, to_push.data, dlc_to_len[to_push.data_len_code]); can_send(&to_send, bus_fwd_num, true); } diff --git a/board/drivers/can_common.h b/board/drivers/can_common.h index 1d915fd1..fac20287 100644 --- a/board/drivers/can_common.h +++ b/board/drivers/can_common.h @@ -2,7 +2,7 @@ typedef struct { volatile uint32_t w_ptr; volatile uint32_t r_ptr; uint32_t fifo_size; - CAN_FIFOMailBox_TypeDef *elems; + CANPacket_t *elems; } can_ring; typedef struct { @@ -12,8 +12,6 @@ typedef struct { uint32_t can_data_speed; } bus_config_t; -#define CAN_BUS_RET_FLAG 0x80U -#define CAN_BUS_BLK_FLAG 0x40U #define CAN_BUS_NUM_MASK 0x3FU #define BUS_MAX 4U @@ -49,19 +47,19 @@ void process_can(uint8_t can_number); // ********************* instantiate queues ********************* #define can_buffer(x, size) \ - CAN_FIFOMailBox_TypeDef elems_##x[size]; \ - can_ring can_##x = { .w_ptr = 0, .r_ptr = 0, .fifo_size = (size), .elems = (CAN_FIFOMailBox_TypeDef *)&(elems_##x) }; + CANPacket_t elems_##x[size]; \ + can_ring can_##x = { .w_ptr = 0, .r_ptr = 0, .fifo_size = (size), .elems = (CANPacket_t *)&(elems_##x) }; #ifdef STM32H7 __attribute__((section(".ram_d1"))) can_buffer(rx_q, 0x1000) -__attribute__((section(".ram_d1"))) can_buffer(txgmlan_q, 0x100) +__attribute__((section(".ram_d1"))) can_buffer(txgmlan_q, 0x1A0) #else can_buffer(rx_q, 0x1000) -can_buffer(txgmlan_q, 0x100) +can_buffer(txgmlan_q, 0x1A0) #endif -can_buffer(tx1_q, 0x100) -can_buffer(tx2_q, 0x100) -can_buffer(tx3_q, 0x100) +can_buffer(tx1_q, 0x1A0) +can_buffer(tx2_q, 0x1A0) +can_buffer(tx3_q, 0x1A0) // FIXME: // cppcheck-suppress misra-c2012-9.3 can_ring *can_queues[] = {&can_tx1_q, &can_tx2_q, &can_tx3_q, &can_txgmlan_q}; @@ -74,7 +72,7 @@ int can_err_cnt = 0; int can_overflow_cnt = 0; // ********************* interrupt safe queue ********************* -bool can_pop(can_ring *q, CAN_FIFOMailBox_TypeDef *elem) { +bool can_pop(can_ring *q, CANPacket_t *elem) { bool ret = 0; ENTER_CRITICAL(); @@ -92,7 +90,7 @@ bool can_pop(can_ring *q, CAN_FIFOMailBox_TypeDef *elem) { return ret; } -bool can_push(can_ring *q, CAN_FIFOMailBox_TypeDef *elem) { +bool can_push(can_ring *q, CANPacket_t *elem) { bool ret = false; uint32_t next_w_ptr; @@ -193,7 +191,7 @@ void can_flip_buses(uint8_t bus1, uint8_t bus2){ bus_config[bus2].can_num_lookup = bus1; } -void ignition_can_hook(CAN_FIFOMailBox_TypeDef *to_push) { +void ignition_can_hook(CANPacket_t *to_push) { int bus = GET_BUS(to_push); int addr = GET_ADDR(to_push); int len = GET_LEN(to_push); @@ -236,12 +234,10 @@ bool can_tx_check_min_slots_free(uint32_t min) { (can_slots_empty(&can_txgmlan_q) >= min); } -void can_send(CAN_FIFOMailBox_TypeDef *to_push, uint8_t bus_number, bool skip_tx_hook) { +void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook) { if (skip_tx_hook || safety_tx_hook(to_push) != 0) { if (bus_number < BUS_MAX) { // add CAN packet to send queue - // bus number isn't passed through - to_push->RDTR &= 0xF; if ((bus_number == 3U) && (bus_config[3].can_num_lookup == 0xFFU)) { gmlan_send_errs += bitbang_gmlan(to_push) ? 0U : 1U; } else { @@ -250,7 +246,7 @@ void can_send(CAN_FIFOMailBox_TypeDef *to_push, uint8_t bus_number, bool skip_tx } } } else { - to_push->RDTR = (to_push->RDTR & 0xFFFF000FU) | ((CAN_BUS_RET_FLAG | CAN_BUS_BLK_FLAG | bus_number) << 4); + to_push->rejected = 1U; can_send_errs += can_push(&can_rx_q, to_push) ? 0U : 1U; } } diff --git a/board/drivers/fdcan.h b/board/drivers/fdcan.h index a01a908d..3d7d376e 100644 --- a/board/drivers/fdcan.h +++ b/board/drivers/fdcan.h @@ -5,6 +5,11 @@ #define BUS_OFF_FAIL_LIMIT 2U uint8_t bus_off_err[] = {0U, 0U, 0U}; +typedef struct { + volatile uint32_t header[2]; + volatile uint32_t data_word[CANPACKET_DATA_SIZE_MAX/4U]; +} canfd_fifo; + FDCAN_GlobalTypeDef *cans[] = {FDCAN1, FDCAN2, FDCAN3}; bool can_set_speed(uint8_t can_number) { @@ -46,32 +51,37 @@ void process_can(uint8_t can_number) { CANx->IR |= FDCAN_IR_TFE; // Clear Tx FIFO Empty flag if ((CANx->TXFQS & FDCAN_TXFQS_TFQF) == 0) { - CAN_FIFOMailBox_TypeDef to_send; + CANPacket_t to_send; if (can_pop(can_queues[bus_number], &to_send)) { can_tx_cnt += 1; uint32_t TxFIFOSA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET) + (FDCAN_RX_FIFO_0_EL_CNT * FDCAN_RX_FIFO_0_EL_SIZE); uint8_t tx_index = (CANx->TXFQS >> FDCAN_TXFQS_TFQPI_Pos) & 0x1F; // only send if we have received a packet - CAN_FIFOMailBox_TypeDef *fifo; - fifo = (CAN_FIFOMailBox_TypeDef *)(TxFIFOSA + (tx_index * FDCAN_TX_FIFO_EL_SIZE)); + canfd_fifo *fifo; + fifo = (canfd_fifo *)(TxFIFOSA + (tx_index * FDCAN_TX_FIFO_EL_SIZE)); - // Convert from "mailbox type" - fifo->RIR = ((to_send.RIR & 0x6) << 28) | (to_send.RIR >> 3); // identifier format and frame type | identifier - //REDEBUG: enable CAN FD and BRS for test purposes - //fifo->RDTR = ((to_send.RDTR & 0xF) << 16) | ((to_send.RDTR) >> 16) | (1U << 21) | (1U << 20); // DLC (length) | timestamp | enable CAN FD | enable BRS - fifo->RDTR = ((to_send.RDTR & 0xF) << 16) | ((to_send.RDTR) >> 16); // DLC (length) | timestamp - fifo->RDLR = to_send.RDLR; - fifo->RDHR = to_send.RDHR; + fifo->header[0] = (to_send.extended << 30) | ((to_send.extended != 0) ? (to_send.addr) : (to_send.addr << 18)); + fifo->header[1] = (to_send.data_len_code << 16); // | (1U << 21) | (1U << 20) to enable CAN FD , enable BRS + + uint8_t data_len_w = (dlc_to_len[to_send.data_len_code] / 4U); + data_len_w += ((dlc_to_len[to_send.data_len_code] % 4) > 0) ? 1U : 0U; + for (unsigned int i = 0; i < data_len_w; i++) { + BYTE_ARRAY_TO_WORD(fifo->data_word[i], &to_send.data[i*4]); + } CANx->TXBAR = (1UL << tx_index); // Send back to USB can_txd_cnt += 1; - CAN_FIFOMailBox_TypeDef to_push; - to_push.RIR = to_send.RIR; - to_push.RDTR = (to_send.RDTR & 0xFFFF000FU) | ((CAN_BUS_RET_FLAG | bus_number) << 4); - to_push.RDLR = to_send.RDLR; - to_push.RDHR = to_send.RDHR; + CANPacket_t to_push; + + to_push.returned = 1U; + to_push.rejected = 0U; + to_push.extended = to_send.extended; + to_push.addr = to_send.addr; + to_push.bus = to_send.bus; + to_push.data_len_code = to_send.data_len_code; + (void)memcpy(to_push.data, to_send.data, dlc_to_len[to_push.data_len_code]); can_send_errs += can_push(&can_rx_q, &to_push) ? 0U : 1U; usb_cb_ep3_out_complete(); @@ -123,29 +133,37 @@ void can_rx(uint8_t can_number) { rx_fifo_idx = (uint8_t)((CANx->RXF0S >> FDCAN_RXF0S_F0GI_Pos) & 0x3F); uint32_t RxFIFO0SA = FDCAN_START_ADDRESS + (can_number * FDCAN_OFFSET); - CAN_FIFOMailBox_TypeDef to_push; - CAN_FIFOMailBox_TypeDef *fifo; + CANPacket_t to_push; + canfd_fifo *fifo; // getting address - fifo = (CAN_FIFOMailBox_TypeDef *)(RxFIFO0SA + (rx_fifo_idx * FDCAN_RX_FIFO_0_EL_SIZE)); + fifo = (canfd_fifo *)(RxFIFO0SA + (rx_fifo_idx * FDCAN_RX_FIFO_0_EL_SIZE)); - // Need to convert real CAN frame format to mailbox "type" - to_push.RIR = ((fifo->RIR >> 28) & 0x6) | (fifo->RIR << 3); // identifier format and frame type | identifier - to_push.RDTR = ((fifo->RDTR >> 16) & 0xF) | (fifo->RDTR << 16); // DLC (length) | timestamp - to_push.RDLR = fifo->RDLR; - to_push.RDHR = fifo->RDHR; + to_push.returned = 0U; + to_push.rejected = 0U; + to_push.extended = (fifo->header[0] >> 30) & 0x1U; + to_push.addr = ((to_push.extended != 0) ? (fifo->header[0] & 0x1FFFFFFFU) : ((fifo->header[0] >> 18) & 0x7FFU)); + to_push.bus = bus_number; + to_push.data_len_code = ((fifo->header[1] >> 16) & 0xFU); - // modify RDTR for our API - to_push.RDTR = (to_push.RDTR & 0xFFFF000F) | (bus_number << 4); + uint8_t data_len_w = (dlc_to_len[to_push.data_len_code] / 4U); + data_len_w += ((dlc_to_len[to_push.data_len_code] % 4) > 0) ? 1U : 0U; + for (unsigned int i = 0; i < data_len_w; i++) { + WORD_TO_BYTE_ARRAY(&to_push.data[i*4], fifo->data_word[i]); + } // forwarding (panda only) int bus_fwd_num = safety_fwd_hook(bus_number, &to_push); if (bus_fwd_num != -1) { - CAN_FIFOMailBox_TypeDef to_send; - to_send.RIR = to_push.RIR; - to_send.RDTR = to_push.RDTR; - to_send.RDLR = to_push.RDLR; - to_send.RDHR = to_push.RDHR; + CANPacket_t to_send; + + to_send.returned = 0U; + to_push.rejected = 0U; + to_send.extended = to_push.extended; + to_send.addr = to_push.addr; + to_send.bus = to_push.bus; + to_send.data_len_code = to_push.data_len_code; + (void)memcpy(to_send.data, to_push.data, dlc_to_len[to_push.data_len_code]); can_send(&to_send, bus_fwd_num, true); } diff --git a/board/drivers/gmlan_alt.h b/board/drivers/gmlan_alt.h index 063ac850..9b95e0dd 100644 --- a/board/drivers/gmlan_alt.h +++ b/board/drivers/gmlan_alt.h @@ -75,7 +75,7 @@ int append_int(char *in, int in_len, int val, int val_len) { return in_len_copy; } -int get_bit_message(char *out, CAN_FIFOMailBox_TypeDef *to_bang) { +int get_bit_message(char *out, CANPacket_t *to_bang) { char pkt[MAX_BITS_CAN_PACKET]; char footer[] = { 1, // CRC delimiter @@ -88,18 +88,18 @@ int get_bit_message(char *out, CAN_FIFOMailBox_TypeDef *to_bang) { int len = 0; // test packet - int dlc_len = to_bang->RDTR & 0xF; + int dlc_len = GET_LEN(to_bang); len = append_int(pkt, len, 0, 1); // Start-of-frame - if ((to_bang->RIR & 4) != 0) { + if (to_bang->extended != 0) { // extended identifier - len = append_int(pkt, len, to_bang->RIR >> 21, 11); // Identifier + len = append_int(pkt, len, GET_ADDR(to_bang) >> 18, 11); // Identifier len = append_int(pkt, len, 3, 2); // SRR+IDE - len = append_int(pkt, len, (to_bang->RIR >> 3) & ((1U << 18) - 1U), 18); // Identifier + len = append_int(pkt, len, (GET_ADDR(to_bang)) & ((1U << 18) - 1U), 18); // Identifier len = append_int(pkt, len, 0, 3); // RTR+r1+r0 } else { // standard identifier - len = append_int(pkt, len, to_bang->RIR >> 21, 11); // Identifier + len = append_int(pkt, len, GET_ADDR(to_bang), 11); // Identifier len = append_int(pkt, len, 0, 3); // RTR+IDE+reserved } @@ -107,8 +107,7 @@ int get_bit_message(char *out, CAN_FIFOMailBox_TypeDef *to_bang) { // append data for (int i = 0; i < dlc_len; i++) { - unsigned char dat = ((unsigned char *)(&(to_bang->RDLR)))[i]; - len = append_int(pkt, len, dat, 8); + len = append_int(pkt, len, to_bang->data[i], 8); } // append crc @@ -269,7 +268,7 @@ void TIM12_IRQ_Handler(void) { TIM12->SR = 0; } -bool bitbang_gmlan(CAN_FIFOMailBox_TypeDef *to_bang) { +bool bitbang_gmlan(CANPacket_t *to_bang) { gmlan_send_ok = true; gmlan_alt_mode = BITBANG; diff --git a/board/drivers/usb.h b/board/drivers/usb.h index 2316f722..6b743bd1 100644 --- a/board/drivers/usb.h +++ b/board/drivers/usb.h @@ -23,7 +23,8 @@ typedef union _USB_Setup { } USB_Setup_TypeDef; -#define MAX_CAN_MSGS_PER_BULK_TRANSFER 4U +#define MAX_CAN_MSGS_PER_BULK_TRANSFER 51U +#define MAX_EP1_CHUNK_PER_BULK_TRANSFER 16256 // max data stream chunk in bytes, shouldn't be higher than 16320 or counter will overflow bool usb_eopf_detected = false; @@ -493,7 +494,7 @@ void usb_setup(void) { USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_USBAEP; USBx_OUTEP(2)->DOEPINT = 0xFF; - USBx_OUTEP(3)->DOEPTSIZ = (1U << 19) | 0x40U; + USBx_OUTEP(3)->DOEPTSIZ = (32U << 19) | 0x800U; USBx_OUTEP(3)->DOEPCTL = (0x40U & USB_OTG_DOEPCTL_MPSIZ) | (2U << 18) | USB_OTG_DOEPCTL_SD0PID_SEVNFRM | USB_OTG_DOEPCTL_USBAEP; USBx_OUTEP(3)->DOEPINT = 0xFF; @@ -934,7 +935,7 @@ void usb_irqhandler(void) { void usb_outep3_resume_if_paused(void) { ENTER_CRITICAL(); if (!outep3_processing && (USBx_OUTEP(3)->DOEPCTL & USB_OTG_DOEPCTL_NAKSTS) != 0) { - USBx_OUTEP(3)->DOEPTSIZ = (1U << 19) | 0x40U; + USBx_OUTEP(3)->DOEPTSIZ = (32U << 19) | 0x800U; USBx_OUTEP(3)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK; } EXIT_CRITICAL(); diff --git a/board/flasher.h b/board/flasher.h index 764eddb9..534b65e7 100644 --- a/board/flasher.h +++ b/board/flasher.h @@ -183,7 +183,7 @@ void CAN1_RX0_IRQ_Handler(void) { if ((CAN->sFIFOMailBox[0].RIR>>21) == CAN_BL_INPUT) { uint8_t dat[8]; for (int i = 0; i < 8; i++) { - dat[i] = GET_BYTE(&CAN->sFIFOMailBox[0], i); + dat[i] = GET_MAILBOX_BYTE(&CAN->sFIFOMailBox[0], i); } uint8_t odat[8]; uint8_t type = dat[0] & 0xF0; diff --git a/board/main.c b/board/main.c index c3b27c58..c0fa83e9 100644 --- a/board/main.c +++ b/board/main.c @@ -20,6 +20,8 @@ #include "drivers/bxcan.h" #endif +#include "usb_protocol.h" + #include "obj/gitversion.h" extern int _app_start[0xc000]; // Only first 3 sectors of size 0x4000 are used @@ -193,15 +195,7 @@ int get_rtc_pkt(void *dat) { return sizeof(t); } -int usb_cb_ep1_in(void *usbdata, int len, bool hardwired) { - UNUSED(hardwired); - CAN_FIFOMailBox_TypeDef *reply = (CAN_FIFOMailBox_TypeDef *)usbdata; - int ilen = 0; - while (ilen < MIN(len/0x10, 4) && can_pop(&can_rx_q, &reply[ilen])) { - ilen++; - } - return ilen*0x10; -} + // send on serial, first byte to select the ring void usb_cb_ep2_out(void *usbdata, int len, bool hardwired) { @@ -219,26 +213,8 @@ void usb_cb_ep2_out(void *usbdata, int len, bool hardwired) { } } -// send on CAN -void usb_cb_ep3_out(void *usbdata, int len, bool hardwired) { - UNUSED(hardwired); - int dpkt = 0; - uint32_t *d32 = (uint32_t *)usbdata; - for (dpkt = 0; dpkt < (len / 4); dpkt += 4) { - CAN_FIFOMailBox_TypeDef to_push; - to_push.RDHR = d32[dpkt + 3]; - to_push.RDLR = d32[dpkt + 2]; - to_push.RDTR = d32[dpkt + 1]; - to_push.RIR = d32[dpkt]; - - uint8_t bus_number = (to_push.RDTR >> 4) & CAN_BUS_NUM_MASK; - can_send(&to_push, bus_number, false); - } -} - void usb_cb_ep3_out_complete(void) { - // TODO: how does a second USB packet sneek in? (why multiply by 2) - if (can_tx_check_min_slots_free(MAX_CAN_MSGS_PER_BULK_TRANSFER * 2U)) { + if (can_tx_check_min_slots_free(MAX_CAN_MSGS_PER_BULK_TRANSFER)) { usb_outep3_resume_if_paused(); } } diff --git a/board/pedal/main.c b/board/pedal/main.c index 32d85aab..7553b3e5 100644 --- a/board/pedal/main.c +++ b/board/pedal/main.c @@ -132,11 +132,11 @@ void CAN1_RX0_IRQ_Handler(void) { int address = CAN->sFIFOMailBox[0].RIR >> 21; if (address == CAN_GAS_INPUT) { // softloader entry - if (GET_BYTES_04(&CAN->sFIFOMailBox[0]) == 0xdeadface) { - if (GET_BYTES_48(&CAN->sFIFOMailBox[0]) == 0x0ab00b1e) { + if (GET_MAILBOX_BYTES_04(&CAN->sFIFOMailBox[0]) == 0xdeadface) { + if (GET_MAILBOX_BYTES_48(&CAN->sFIFOMailBox[0]) == 0x0ab00b1e) { enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; NVIC_SystemReset(); - } else if (GET_BYTES_48(&CAN->sFIFOMailBox[0]) == 0x02b00b1e) { + } else if (GET_MAILBOX_BYTES_48(&CAN->sFIFOMailBox[0]) == 0x02b00b1e) { enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; NVIC_SystemReset(); } else { @@ -147,7 +147,7 @@ void CAN1_RX0_IRQ_Handler(void) { // normal packet uint8_t dat[8]; for (int i=0; i<8; i++) { - dat[i] = GET_BYTE(&CAN->sFIFOMailBox[0], i); + dat[i] = GET_MAILBOX_BYTE(&CAN->sFIFOMailBox[0], i); } uint16_t value_0 = (dat[0] << 8) | dat[1]; uint16_t value_1 = (dat[2] << 8) | dat[3]; diff --git a/board/safety.h b/board/safety.h index 951eede7..fd151627 100644 --- a/board/safety.h +++ b/board/safety.h @@ -44,11 +44,11 @@ int16_t current_safety_param = 0; const safety_hooks *current_hooks = &nooutput_hooks; const addr_checks *current_rx_checks = &default_rx_checks; -int safety_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { +int safety_rx_hook(CANPacket_t *to_push) { return current_hooks->rx(to_push); } -int safety_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +int safety_tx_hook(CANPacket_t *to_send) { return (relay_malfunction ? -1 : current_hooks->tx(to_send)); } @@ -56,7 +56,7 @@ int safety_tx_lin_hook(int lin_num, uint8_t *data, int len) { return current_hooks->tx_lin(lin_num, data, len); } -int safety_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { +int safety_fwd_hook(int bus_num, CANPacket_t *to_fwd) { return (relay_malfunction ? -1 : current_hooks->fwd(bus_num, to_fwd)); } @@ -75,7 +75,7 @@ void gen_crc_lookup_table(uint8_t poly, uint8_t crc_lut[]) { } } -bool msg_allowed(CAN_FIFOMailBox_TypeDef *to_send, const CanMsg msg_list[], int len) { +bool msg_allowed(CANPacket_t *to_send, const CanMsg msg_list[], int len) { int addr = GET_ADDR(to_send); int bus = GET_BUS(to_send); int length = GET_LEN(to_send); @@ -96,7 +96,7 @@ uint32_t get_ts_elapsed(uint32_t ts, uint32_t ts_last) { return ts - ts_last; } -int get_addr_check_index(CAN_FIFOMailBox_TypeDef *to_push, AddrCheckStruct addr_list[], const int len) { +int get_addr_check_index(CANPacket_t *to_push, AddrCheckStruct addr_list[], const int len) { int bus = GET_BUS(to_push); int addr = GET_ADDR(to_push); int length = GET_LEN(to_push); @@ -170,11 +170,11 @@ void update_addr_timestamp(AddrCheckStruct addr_list[], int index) { } } -bool addr_safety_check(CAN_FIFOMailBox_TypeDef *to_push, +bool addr_safety_check(CANPacket_t *to_push, const addr_checks *rx_checks, - uint8_t (*get_checksum)(CAN_FIFOMailBox_TypeDef *to_push), - uint8_t (*compute_checksum)(CAN_FIFOMailBox_TypeDef *to_push), - uint8_t (*get_counter)(CAN_FIFOMailBox_TypeDef *to_push)) { + uint8_t (*get_checksum)(CANPacket_t *to_push), + uint8_t (*compute_checksum)(CANPacket_t *to_push), + uint8_t (*get_counter)(CANPacket_t *to_push)) { int index = get_addr_check_index(to_push, rx_checks->check, rx_checks->len); update_addr_timestamp(rx_checks->check, index); diff --git a/board/safety/safety_chrysler.h b/board/safety/safety_chrysler.h index ad30f4ca..35474f34 100644 --- a/board/safety/safety_chrysler.h +++ b/board/safety/safety_chrysler.h @@ -18,12 +18,12 @@ AddrCheckStruct chrysler_addr_checks[] = { #define CHRYSLER_ADDR_CHECK_LEN (sizeof(chrysler_addr_checks) / sizeof(chrysler_addr_checks[0])) addr_checks chrysler_rx_checks = {chrysler_addr_checks, CHRYSLER_ADDR_CHECK_LEN}; -static uint8_t chrysler_get_checksum(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t chrysler_get_checksum(CANPacket_t *to_push) { int checksum_byte = GET_LEN(to_push) - 1; return (uint8_t)(GET_BYTE(to_push, checksum_byte)); } -static uint8_t chrysler_compute_checksum(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t chrysler_compute_checksum(CANPacket_t *to_push) { /* This function does not want the checksum byte in the input data. jeep chrysler canbus checksum from http://illmatics.com/Remote%20Car%20Hacking.pdf */ uint8_t checksum = 0xFFU; @@ -56,12 +56,12 @@ static uint8_t chrysler_compute_checksum(CAN_FIFOMailBox_TypeDef *to_push) { return ~checksum; } -static uint8_t chrysler_get_counter(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t chrysler_get_counter(CANPacket_t *to_push) { // Well defined counter only for 8 bytes messages return (uint8_t)(GET_BYTE(to_push, 6) >> 4); } -static int chrysler_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { +static int chrysler_rx_hook(CANPacket_t *to_push) { bool valid = addr_safety_check(to_push, &chrysler_rx_checks, chrysler_get_checksum, chrysler_compute_checksum, @@ -117,7 +117,7 @@ static int chrysler_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { return valid; } -static int chrysler_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +static int chrysler_tx_hook(CANPacket_t *to_send) { int tx = 1; int addr = GET_ADDR(to_send); @@ -182,7 +182,7 @@ static int chrysler_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { return tx; } -static int chrysler_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { +static int chrysler_fwd_hook(int bus_num, CANPacket_t *to_fwd) { int bus_fwd = -1; int addr = GET_ADDR(to_fwd); diff --git a/board/safety/safety_defaults.h b/board/safety/safety_defaults.h index cdbb656e..409ab0c8 100644 --- a/board/safety/safety_defaults.h +++ b/board/safety/safety_defaults.h @@ -3,7 +3,7 @@ const addr_checks default_rx_checks = { .len = 0, }; -int default_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { +int default_rx_hook(CANPacket_t *to_push) { UNUSED(to_push); return true; } @@ -17,7 +17,7 @@ static const addr_checks* nooutput_init(int16_t param) { return &default_rx_checks; } -static int nooutput_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +static int nooutput_tx_hook(CANPacket_t *to_send) { UNUSED(to_send); return false; } @@ -29,7 +29,7 @@ static int nooutput_tx_lin_hook(int lin_num, uint8_t *data, int len) { return false; } -static int default_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { +static int default_fwd_hook(int bus_num, CANPacket_t *to_fwd) { UNUSED(bus_num); UNUSED(to_fwd); return -1; @@ -52,7 +52,7 @@ static const addr_checks* alloutput_init(int16_t param) { return &default_rx_checks; } -static int alloutput_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +static int alloutput_tx_hook(CANPacket_t *to_send) { UNUSED(to_send); return true; } diff --git a/board/safety/safety_elm327.h b/board/safety/safety_elm327.h index e6655c3d..d4f562a8 100644 --- a/board/safety/safety_elm327.h +++ b/board/safety/safety_elm327.h @@ -1,4 +1,4 @@ -static int elm327_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +static int elm327_tx_hook(CANPacket_t *to_send) { int tx = 1; int addr = GET_ADDR(to_send); diff --git a/board/safety/safety_ford.h b/board/safety/safety_ford.h index cc771c44..6a8f0583 100644 --- a/board/safety/safety_ford.h +++ b/board/safety/safety_ford.h @@ -8,7 +8,7 @@ // brake > 0mph -static int ford_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { +static int ford_rx_hook(CANPacket_t *to_push) { int addr = GET_ADDR(to_push); int bus = GET_BUS(to_push); @@ -65,7 +65,7 @@ static int ford_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { // else // block all commands that produce actuation -static int ford_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +static int ford_tx_hook(CANPacket_t *to_send) { int tx = 1; int addr = GET_ADDR(to_send); diff --git a/board/safety/safety_gm.h b/board/safety/safety_gm.h index efe749a3..6ef30f27 100644 --- a/board/safety/safety_gm.h +++ b/board/safety/safety_gm.h @@ -34,7 +34,7 @@ AddrCheckStruct gm_addr_checks[] = { #define GM_RX_CHECK_LEN (sizeof(gm_addr_checks) / sizeof(gm_addr_checks[0])) addr_checks gm_rx_checks = {gm_addr_checks, GM_RX_CHECK_LEN}; -static int gm_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { +static int gm_rx_hook(CANPacket_t *to_push) { bool valid = addr_safety_check(to_push, &gm_rx_checks, NULL, NULL, NULL); @@ -104,7 +104,7 @@ static int gm_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { // else // block all commands that produce actuation -static int gm_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +static int gm_tx_hook(CANPacket_t *to_send) { int tx = 1; int addr = GET_ADDR(to_send); diff --git a/board/safety/safety_honda.h b/board/safety/safety_honda.h index 89e2b5a0..8b797e67 100644 --- a/board/safety/safety_honda.h +++ b/board/safety/safety_honda.h @@ -53,12 +53,12 @@ enum {HONDA_N_HW, HONDA_BG_HW, HONDA_BH_HW} honda_hw = HONDA_N_HW; addr_checks honda_rx_checks = {honda_addr_checks, HONDA_ADDR_CHECKS_LEN}; -static uint8_t honda_get_checksum(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t honda_get_checksum(CANPacket_t *to_push) { int checksum_byte = GET_LEN(to_push) - 1; return (uint8_t)(GET_BYTE(to_push, checksum_byte)) & 0xFU; } -static uint8_t honda_compute_checksum(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t honda_compute_checksum(CANPacket_t *to_push) { int len = GET_LEN(to_push); uint8_t checksum = 0U; unsigned int addr = GET_ADDR(to_push); @@ -75,12 +75,12 @@ static uint8_t honda_compute_checksum(CAN_FIFOMailBox_TypeDef *to_push) { return (8U - checksum) & 0xFU; } -static uint8_t honda_get_counter(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t honda_get_counter(CANPacket_t *to_push) { int counter_byte = GET_LEN(to_push) - 1; return ((uint8_t)(GET_BYTE(to_push, counter_byte)) >> 4U) & 0x3U; } -static int honda_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { +static int honda_rx_hook(CANPacket_t *to_push) { bool valid = addr_safety_check(to_push, &honda_rx_checks, honda_get_checksum, honda_compute_checksum, honda_get_counter); @@ -186,7 +186,7 @@ static int honda_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { // else // block all commands that produce actuation -static int honda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +static int honda_tx_hook(CANPacket_t *to_send) { int tx = 1; int addr = GET_ADDR(to_send); @@ -345,7 +345,7 @@ static const addr_checks* honda_bosch_harness_init(int16_t param) { return &honda_rx_checks; } -static int honda_nidec_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { +static int honda_nidec_fwd_hook(int bus_num, CANPacket_t *to_fwd) { // fwd from car to camera. also fwd certain msgs from camera to car // 0xE4 is steering on all cars except CRV and RDX, 0x194 for CRV and RDX, // 0x1FA is brake control, 0x30C is acc hud, 0x33D is lkas hud, @@ -370,7 +370,7 @@ static int honda_nidec_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { return bus_fwd; } -static int honda_bosch_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { +static int honda_bosch_fwd_hook(int bus_num, CANPacket_t *to_fwd) { int bus_fwd = -1; int bus_rdr_cam = (honda_hw == HONDA_BH_HW) ? 2 : 1; // radar bus, camera side int bus_rdr_car = (honda_hw == HONDA_BH_HW) ? 0 : 2; // radar bus, car side diff --git a/board/safety/safety_hyundai.h b/board/safety/safety_hyundai.h index 59b56fa1..4e4f0e65 100644 --- a/board/safety/safety_hyundai.h +++ b/board/safety/safety_hyundai.h @@ -69,7 +69,7 @@ bool hyundai_longitudinal = false; addr_checks hyundai_rx_checks = {hyundai_addr_checks, HYUNDAI_ADDR_CHECK_LEN}; -static uint8_t hyundai_get_counter(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t hyundai_get_counter(CANPacket_t *to_push) { int addr = GET_ADDR(to_push); uint8_t cnt; @@ -89,7 +89,7 @@ static uint8_t hyundai_get_counter(CAN_FIFOMailBox_TypeDef *to_push) { return cnt; } -static uint8_t hyundai_get_checksum(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t hyundai_get_checksum(CANPacket_t *to_push) { int addr = GET_ADDR(to_push); uint8_t chksum; @@ -107,7 +107,7 @@ static uint8_t hyundai_get_checksum(CAN_FIFOMailBox_TypeDef *to_push) { return chksum; } -static uint8_t hyundai_compute_checksum(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t hyundai_compute_checksum(CANPacket_t *to_push) { int addr = GET_ADDR(to_push); uint8_t chksum = 0; @@ -143,7 +143,7 @@ static uint8_t hyundai_compute_checksum(CAN_FIFOMailBox_TypeDef *to_push) { return chksum; } -static int hyundai_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { +static int hyundai_rx_hook(CANPacket_t *to_push) { bool valid = addr_safety_check(to_push, &hyundai_rx_checks, hyundai_get_checksum, hyundai_compute_checksum, @@ -223,7 +223,7 @@ static int hyundai_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { return valid; } -static int hyundai_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +static int hyundai_tx_hook(CANPacket_t *to_send) { int tx = 1; int addr = GET_ADDR(to_send); @@ -338,7 +338,7 @@ static int hyundai_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { return tx; } -static int hyundai_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { +static int hyundai_fwd_hook(int bus_num, CANPacket_t *to_fwd) { int bus_fwd = -1; int addr = GET_ADDR(to_fwd); diff --git a/board/safety/safety_mazda.h b/board/safety/safety_mazda.h index 07a8ef09..01cc29f1 100644 --- a/board/safety/safety_mazda.h +++ b/board/safety/safety_mazda.h @@ -37,7 +37,7 @@ AddrCheckStruct mazda_addr_checks[] = { addr_checks mazda_rx_checks = {mazda_addr_checks, MAZDA_ADDR_CHECKS_LEN}; // track msgs coming from OP so that we know what CAM msgs to drop and what to forward -static int mazda_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { +static int mazda_rx_hook(CANPacket_t *to_push) { bool valid = addr_safety_check(to_push, &mazda_rx_checks, NULL, NULL, NULL); if (valid && (GET_BUS(to_push) == MAZDA_MAIN)) { int addr = GET_ADDR(to_push); @@ -80,7 +80,7 @@ static int mazda_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { return valid; } -static int mazda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +static int mazda_tx_hook(CANPacket_t *to_send) { int tx = 1; int addr = GET_ADDR(to_send); int bus = GET_BUS(to_send); @@ -153,7 +153,7 @@ static int mazda_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { return tx; } -static int mazda_fwd_hook(int bus, CAN_FIFOMailBox_TypeDef *to_fwd) { +static int mazda_fwd_hook(int bus, CANPacket_t *to_fwd) { int bus_fwd = -1; int addr = GET_ADDR(to_fwd); diff --git a/board/safety/safety_nissan.h b/board/safety/safety_nissan.h index af170770..2c2c6f68 100644 --- a/board/safety/safety_nissan.h +++ b/board/safety/safety_nissan.h @@ -41,7 +41,7 @@ addr_checks nissan_rx_checks = {nissan_addr_checks, NISSAN_ADDR_CHECK_LEN}; // EPS Location. false = V-CAN, true = C-CAN bool nissan_alt_eps = false; -static int nissan_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { +static int nissan_rx_hook(CANPacket_t *to_push) { bool valid = addr_safety_check(to_push, &nissan_rx_checks, NULL, NULL, NULL); @@ -106,7 +106,7 @@ static int nissan_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { } -static int nissan_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +static int nissan_tx_hook(CANPacket_t *to_send) { int tx = 1; int addr = GET_ADDR(to_send); bool violation = 0; @@ -166,7 +166,7 @@ static int nissan_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { } -static int nissan_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { +static int nissan_fwd_hook(int bus_num, CANPacket_t *to_fwd) { int bus_fwd = -1; int addr = GET_ADDR(to_fwd); diff --git a/board/safety/safety_subaru.h b/board/safety/safety_subaru.h index 09f7f9b9..36eecb51 100644 --- a/board/safety/safety_subaru.h +++ b/board/safety/safety_subaru.h @@ -37,15 +37,15 @@ AddrCheckStruct subaru_l_addr_checks[] = { #define SUBARU_L_ADDR_CHECK_LEN (sizeof(subaru_l_addr_checks) / sizeof(subaru_l_addr_checks[0])) addr_checks subaru_l_rx_checks = {subaru_l_addr_checks, SUBARU_L_ADDR_CHECK_LEN}; -static uint8_t subaru_get_checksum(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t subaru_get_checksum(CANPacket_t *to_push) { return (uint8_t)GET_BYTE(to_push, 0); } -static uint8_t subaru_get_counter(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t subaru_get_counter(CANPacket_t *to_push) { return (uint8_t)(GET_BYTE(to_push, 1) & 0xF); } -static uint8_t subaru_compute_checksum(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t subaru_compute_checksum(CANPacket_t *to_push) { int addr = GET_ADDR(to_push); int len = GET_LEN(to_push); uint8_t checksum = (uint8_t)(addr) + (uint8_t)((unsigned int)(addr) >> 8U); @@ -55,7 +55,7 @@ static uint8_t subaru_compute_checksum(CAN_FIFOMailBox_TypeDef *to_push) { return checksum; } -static int subaru_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { +static int subaru_rx_hook(CANPacket_t *to_push) { bool valid = addr_safety_check(to_push, &subaru_rx_checks, subaru_get_checksum, subaru_compute_checksum, subaru_get_counter); @@ -102,7 +102,7 @@ static int subaru_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { return valid; } -static int subaru_legacy_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { +static int subaru_legacy_rx_hook(CANPacket_t *to_push) { bool valid = addr_safety_check(to_push, &subaru_l_rx_checks, NULL, NULL, NULL); @@ -148,7 +148,7 @@ static int subaru_legacy_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { return valid; } -static int subaru_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +static int subaru_tx_hook(CANPacket_t *to_send) { int tx = 1; int addr = GET_ADDR(to_send); @@ -208,7 +208,7 @@ static int subaru_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { return tx; } -static int subaru_legacy_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +static int subaru_legacy_tx_hook(CANPacket_t *to_send) { int tx = 1; int addr = GET_ADDR(to_send); @@ -268,7 +268,7 @@ static int subaru_legacy_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { return tx; } -static int subaru_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { +static int subaru_fwd_hook(int bus_num, CANPacket_t *to_fwd) { int bus_fwd = -1; if (bus_num == 0) { @@ -290,7 +290,7 @@ static int subaru_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { return bus_fwd; } -static int subaru_legacy_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { +static int subaru_legacy_fwd_hook(int bus_num, CANPacket_t *to_fwd) { int bus_fwd = -1; if (bus_num == 0) { diff --git a/board/safety/safety_tesla.h b/board/safety/safety_tesla.h index 9cca63ae..aa771d35 100644 --- a/board/safety/safety_tesla.h +++ b/board/safety/safety_tesla.h @@ -28,7 +28,7 @@ addr_checks tesla_rx_checks = {tesla_addr_checks, TESLA_ADDR_CHECK_LEN}; bool autopilot_enabled = false; -static int tesla_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { +static int tesla_rx_hook(CANPacket_t *to_push) { bool valid = addr_safety_check(to_push, &tesla_rx_checks, NULL, NULL, NULL); @@ -101,7 +101,7 @@ static int tesla_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { } -static int tesla_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +static int tesla_tx_hook(CANPacket_t *to_send) { int tx = 1; int addr = GET_ADDR(to_send); bool violation = false; @@ -162,7 +162,7 @@ static int tesla_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { return tx; } -static int tesla_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { +static int tesla_fwd_hook(int bus_num, CANPacket_t *to_fwd) { int bus_fwd = -1; int addr = GET_ADDR(to_fwd); diff --git a/board/safety/safety_toyota.h b/board/safety/safety_toyota.h index 401f9559..843acab0 100644 --- a/board/safety/safety_toyota.h +++ b/board/safety/safety_toyota.h @@ -44,7 +44,7 @@ addr_checks toyota_rx_checks = {toyota_addr_checks, TOYOTA_ADDR_CHECKS_LEN}; // global actuation limit states int toyota_dbc_eps_torque_factor = 100; // conversion factor for STEER_TORQUE_EPS in %: see dbc file -static uint8_t toyota_compute_checksum(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t toyota_compute_checksum(CANPacket_t *to_push) { int addr = GET_ADDR(to_push); int len = GET_LEN(to_push); uint8_t checksum = (uint8_t)(addr) + (uint8_t)((unsigned int)(addr) >> 8U) + (uint8_t)(len); @@ -54,12 +54,12 @@ static uint8_t toyota_compute_checksum(CAN_FIFOMailBox_TypeDef *to_push) { return checksum; } -static uint8_t toyota_get_checksum(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t toyota_get_checksum(CANPacket_t *to_push) { int checksum_byte = GET_LEN(to_push) - 1; return (uint8_t)(GET_BYTE(to_push, checksum_byte)); } -static int toyota_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { +static int toyota_rx_hook(CANPacket_t *to_push) { bool valid = addr_safety_check(to_push, &toyota_rx_checks, toyota_get_checksum, toyota_compute_checksum, NULL); @@ -134,7 +134,7 @@ static int toyota_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { return valid; } -static int toyota_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +static int toyota_tx_hook(CANPacket_t *to_send) { int tx = 1; int addr = GET_ADDR(to_send); @@ -247,7 +247,8 @@ static const addr_checks* toyota_init(int16_t param) { return &toyota_rx_checks; } -static int toyota_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { +static int toyota_fwd_hook(int bus_num, CANPacket_t *to_fwd) { + int bus_fwd = -1; if (bus_num == 0) { diff --git a/board/safety/safety_volkswagen.h b/board/safety/safety_volkswagen.h index d7c06a36..42c6cac2 100644 --- a/board/safety/safety_volkswagen.h +++ b/board/safety/safety_volkswagen.h @@ -58,22 +58,22 @@ int volkswagen_lane_msg = 0; uint8_t volkswagen_crc8_lut_8h2f[256]; // Static lookup table for CRC8 poly 0x2F, aka 8H2F/AUTOSAR -static uint8_t volkswagen_get_checksum(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t volkswagen_get_checksum(CANPacket_t *to_push) { return (uint8_t)GET_BYTE(to_push, 0); } -static uint8_t volkswagen_mqb_get_counter(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t volkswagen_mqb_get_counter(CANPacket_t *to_push) { // MQB message counters are consistently found at LSB 8. return (uint8_t)GET_BYTE(to_push, 1) & 0xFU; } -static uint8_t volkswagen_pq_get_counter(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t volkswagen_pq_get_counter(CANPacket_t *to_push) { // Few PQ messages have counters, and their offsets are inconsistent. This // function works only for Lenkhilfe_3 at this time. return (uint8_t)(GET_BYTE(to_push, 1) & 0xF0U) >> 4; } -static uint8_t volkswagen_mqb_compute_crc(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t volkswagen_mqb_compute_crc(CANPacket_t *to_push) { int addr = GET_ADDR(to_push); int len = GET_LEN(to_push); @@ -108,7 +108,7 @@ static uint8_t volkswagen_mqb_compute_crc(CAN_FIFOMailBox_TypeDef *to_push) { return crc ^ 0xFFU; } -static uint8_t volkswagen_pq_compute_checksum(CAN_FIFOMailBox_TypeDef *to_push) { +static uint8_t volkswagen_pq_compute_checksum(CANPacket_t *to_push) { int len = GET_LEN(to_push); uint8_t checksum = 0U; @@ -140,7 +140,7 @@ static const addr_checks* volkswagen_pq_init(int16_t param) { return &volkswagen_pq_rx_checks; } -static int volkswagen_mqb_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { +static int volkswagen_mqb_rx_hook(CANPacket_t *to_push) { bool valid = addr_safety_check(to_push, &volkswagen_mqb_rx_checks, volkswagen_get_checksum, volkswagen_mqb_compute_crc, volkswagen_mqb_get_counter); @@ -200,7 +200,7 @@ static int volkswagen_mqb_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { return valid; } -static int volkswagen_pq_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { +static int volkswagen_pq_rx_hook(CANPacket_t *to_push) { bool valid = addr_safety_check(to_push, &volkswagen_pq_rx_checks, volkswagen_get_checksum, volkswagen_pq_compute_checksum, volkswagen_pq_get_counter); @@ -297,7 +297,7 @@ static bool volkswagen_steering_check(int desired_torque) { return violation; } -static int volkswagen_mqb_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +static int volkswagen_mqb_tx_hook(CANPacket_t *to_send) { int addr = GET_ADDR(to_send); int tx = 1; @@ -333,7 +333,7 @@ static int volkswagen_mqb_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { return tx; } -static int volkswagen_pq_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { +static int volkswagen_pq_tx_hook(CANPacket_t *to_send) { int addr = GET_ADDR(to_send); int tx = 1; @@ -370,7 +370,7 @@ static int volkswagen_pq_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { return tx; } -static int volkswagen_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { +static int volkswagen_fwd_hook(int bus_num, CANPacket_t *to_fwd) { int addr = GET_ADDR(to_fwd); int bus_fwd = -1; diff --git a/board/safety_declarations.h b/board/safety_declarations.h index a502a320..4754738f 100644 --- a/board/safety_declarations.h +++ b/board/safety_declarations.h @@ -1,3 +1,8 @@ +#define GET_BYTE(msg, b) ((msg)->data[(b)]) +#define GET_BYTES_04(msg) ((msg)->data[0] | ((msg)->data[1] << 8) | ((msg)->data[2] << 16) | ((msg)->data[3] << 24)) +#define GET_BYTES_48(msg) ((msg)->data[4] | ((msg)->data[5] << 8) | ((msg)->data[6] << 16) | ((msg)->data[7] << 24)) +#define GET_FLAG(value, mask) (((__typeof__(mask))(value) & (mask)) == (mask)) + const int MAX_WRONG_COUNTERS = 5; const uint8_t MAX_MISSED_MSGS = 10U; @@ -48,8 +53,8 @@ typedef struct { int len; } addr_checks; -int safety_rx_hook(CAN_FIFOMailBox_TypeDef *to_push); -int safety_tx_hook(CAN_FIFOMailBox_TypeDef *to_send); +int safety_rx_hook(CANPacket_t *to_push); +int safety_tx_hook(CANPacket_t *to_send); int safety_tx_lin_hook(int lin_num, uint8_t *data, int len); uint32_t get_ts_elapsed(uint32_t ts, uint32_t ts_last); int to_signed(int d, int bits); @@ -63,25 +68,25 @@ bool driver_limit_check(int val, int val_last, struct sample_t *val_driver, bool rt_rate_limit_check(int val, int val_last, const int MAX_RT_DELTA); float interpolate(struct lookup_t xy, float x); void gen_crc_lookup_table(uint8_t poly, uint8_t crc_lut[]); -bool msg_allowed(CAN_FIFOMailBox_TypeDef *to_send, const CanMsg msg_list[], int len); -int get_addr_check_index(CAN_FIFOMailBox_TypeDef *to_push, AddrCheckStruct addr_list[], const int len); +bool msg_allowed(CANPacket_t *to_send, const CanMsg msg_list[], int len); +int get_addr_check_index(CANPacket_t *to_push, AddrCheckStruct addr_list[], const int len); void update_counter(AddrCheckStruct addr_list[], int index, uint8_t counter); void update_addr_timestamp(AddrCheckStruct addr_list[], int index); bool is_msg_valid(AddrCheckStruct addr_list[], int index); -bool addr_safety_check(CAN_FIFOMailBox_TypeDef *to_push, +bool addr_safety_check(CANPacket_t *to_push, const addr_checks *rx_checks, - uint8_t (*get_checksum)(CAN_FIFOMailBox_TypeDef *to_push), - uint8_t (*compute_checksum)(CAN_FIFOMailBox_TypeDef *to_push), - uint8_t (*get_counter)(CAN_FIFOMailBox_TypeDef *to_push)); + uint8_t (*get_checksum)(CANPacket_t *to_push), + uint8_t (*compute_checksum)(CANPacket_t *to_push), + uint8_t (*get_counter)(CANPacket_t *to_push)); void generic_rx_checks(bool stock_ecu_detected); void relay_malfunction_set(void); void relay_malfunction_reset(void); typedef const addr_checks* (*safety_hook_init)(int16_t param); -typedef int (*rx_hook)(CAN_FIFOMailBox_TypeDef *to_push); -typedef int (*tx_hook)(CAN_FIFOMailBox_TypeDef *to_send); +typedef int (*rx_hook)(CANPacket_t *to_push); +typedef int (*tx_hook)(CANPacket_t *to_send); typedef int (*tx_lin_hook)(int lin_num, uint8_t *data, int len); -typedef int (*fwd_hook)(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd); +typedef int (*fwd_hook)(int bus_num, CANPacket_t *to_fwd); typedef struct { safety_hook_init init; diff --git a/board/stm32fx/stm32fx_config.h b/board/stm32fx/stm32fx_config.h index 0777d2eb..8c5ca1c7 100644 --- a/board/stm32fx/stm32fx_config.h +++ b/board/stm32fx/stm32fx_config.h @@ -38,6 +38,10 @@ #define PROVISION_CHUNK_ADDRESS 0x1FFF79E0U #define DEVICE_SERIAL_NUMBER_ADDRESS 0x1FFF79C0U +#define CANPACKET_DATA_SIZE_MAX 8U + +#include "can_definitions.h" + #ifndef BOOTSTUB #ifdef PANDA #include "main_declarations.h" diff --git a/board/stm32h7/llfdcan.h b/board/stm32h7/llfdcan.h index 2750f642..c3afadae 100644 --- a/board/stm32h7/llfdcan.h +++ b/board/stm32h7/llfdcan.h @@ -33,13 +33,6 @@ #define CAN_NAME_FROM_CANIF(CAN_DEV) (((CAN_DEV)==FDCAN1) ? "FDCAN1" : (((CAN_DEV) == FDCAN2) ? "FDCAN2" : "FDCAN3")) #define CAN_NUM_FROM_CANIF(CAN_DEV) (((CAN_DEV)==FDCAN1) ? 0UL : (((CAN_DEV) == FDCAN2) ? 1UL : 2UL)) -// For backwards compatibility with safety code -typedef struct { - __IO uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ - __IO uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ - __IO uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ - __IO uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ -} CAN_FIFOMailBox_TypeDef; void puts(const char *a); diff --git a/board/stm32h7/stm32h7_config.h b/board/stm32h7/stm32h7_config.h index a295d18a..cdc2a9dc 100644 --- a/board/stm32h7/stm32h7_config.h +++ b/board/stm32h7/stm32h7_config.h @@ -34,6 +34,10 @@ #define PROVISION_CHUNK_ADDRESS 0x080FFFE0U #define DEVICE_SERIAL_NUMBER_ADDRESS 0x080FFFC0U +#define CANPACKET_DATA_SIZE_MAX 64U + +#include "can_definitions.h" + #ifndef BOOTSTUB #include "main_declarations.h" #else diff --git a/board/usb_protocol.h b/board/usb_protocol.h new file mode 100644 index 00000000..0e50aa7a --- /dev/null +++ b/board/usb_protocol.h @@ -0,0 +1,108 @@ +typedef struct { + uint8_t ptr; + uint8_t tail_size; + uint8_t data[72]; + uint8_t counter; +} usb_asm_buffer; + +usb_asm_buffer ep1_buffer = {.ptr = 0, .tail_size = 0, .counter = 0}; +uint32_t total_rx_size = 0; + +int usb_cb_ep1_in(void *usbdata, int len, bool hardwired) { + UNUSED(hardwired); + uint8_t pos = 1; + uint8_t *usbdata8 = (uint8_t *)usbdata; + usbdata8[0] = ep1_buffer.counter; + // Send tail of previous message if it is in buffer + if (ep1_buffer.ptr > 0) { + if (ep1_buffer.ptr <= 63U) { + (void)memcpy(&usbdata8[pos], ep1_buffer.data, ep1_buffer.ptr); + pos += ep1_buffer.ptr; + ep1_buffer.ptr = 0; + } else { + (void)memcpy(&usbdata8[pos], ep1_buffer.data, 63U); + ep1_buffer.ptr = ep1_buffer.ptr - 63U; + (void)memcpy(ep1_buffer.data, &ep1_buffer.data[63U], ep1_buffer.ptr); + pos += 63U; + } + } + + if (total_rx_size > MAX_EP1_CHUNK_PER_BULK_TRANSFER) { + total_rx_size = 0; + ep1_buffer.counter = 0; + } else { + CANPacket_t can_packet; + while ((pos < len) && can_pop(&can_rx_q, &can_packet)) { + uint8_t pckt_len = CANPACKET_HEAD_SIZE + dlc_to_len[can_packet.data_len_code]; + if ((pos + pckt_len) <= len) { + (void)memcpy(&usbdata8[pos], &can_packet, pckt_len); + pos += pckt_len; + } else { + (void)memcpy(&usbdata8[pos], &can_packet, len - pos); + ep1_buffer.ptr = pckt_len - (len - pos); + //(void)memcpy(ep1_buffer.data, ((uint8_t*)&can_packet + (len - pos)), ep1_buffer.ptr); + // cppcheck-suppress objectIndex + (void)memcpy(ep1_buffer.data, &((uint8_t*)&can_packet)[(len - pos)], ep1_buffer.ptr); + pos = len; + } + } + ep1_buffer.counter++; + total_rx_size += pos; + } + if (pos != len) { + ep1_buffer.counter = 0; + total_rx_size = 0; + } + if (pos <= 1) { pos = 0; } + return pos; +} + +usb_asm_buffer ep3_buffer = {.ptr = 0, .tail_size = 0, .counter = 0}; + +// send on CAN +void usb_cb_ep3_out(void *usbdata, int len, bool hardwired) { + UNUSED(hardwired); + uint8_t *usbdata8 = (uint8_t *)usbdata; + // Got first packet from a stream, resetting buffer and counter + if (usbdata8[0] == 0) { + ep3_buffer.counter = 0; + ep3_buffer.ptr = 0; + ep3_buffer.tail_size = 0; + } + // Assembling can message with data from buffer + if (usbdata8[0] == ep3_buffer.counter) { + uint8_t pos = 1; + ep3_buffer.counter++; + if (ep3_buffer.ptr != 0) { + if (ep3_buffer.tail_size <= 63U) { + CANPacket_t to_push; + (void)memcpy(&ep3_buffer.data[ep3_buffer.ptr], &usbdata8[pos], ep3_buffer.tail_size); + (void)memcpy(&to_push, ep3_buffer.data, ep3_buffer.ptr + ep3_buffer.tail_size); + can_send(&to_push, to_push.bus, false); + pos += ep3_buffer.tail_size; + ep3_buffer.ptr = 0; + ep3_buffer.tail_size = 0; + } else { + (void)memcpy(&ep3_buffer.data[ep3_buffer.ptr], &usbdata8[pos], len - pos); + ep3_buffer.tail_size -= 63U; + ep3_buffer.ptr += 63U; + pos += 63U; + } + } + + while (pos < len) { + uint8_t pckt_len = CANPACKET_HEAD_SIZE + dlc_to_len[(usbdata8[pos] >> 4U)]; + if ((pos + pckt_len) <= (uint8_t)len) { + CANPacket_t to_push; + (void)memcpy(&to_push, &usbdata8[pos], pckt_len); + can_send(&to_push, to_push.bus, false); + pos += pckt_len; + } else { + (void)memcpy(ep3_buffer.data, &usbdata8[pos], len - pos); + ep3_buffer.ptr = len - pos; + ep3_buffer.tail_size = pckt_len - ep3_buffer.ptr; + pos += ep3_buffer.ptr; + } + } + } +} diff --git a/python/__init__.py b/python/__init__.py index 597f6e9f..fb5dc481 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -16,26 +16,83 @@ from .serial import PandaSerial # noqa pylint: disable=import-error from .isotp import isotp_send, isotp_recv # pylint: disable=import-error from .config import DEFAULT_FW_FN, DEFAULT_H7_FW_FN # noqa pylint: disable=import-error -__version__ = '0.0.9' +__version__ = '0.0.10' BASEDIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../") DEBUG = os.getenv("PANDADEBUG") is not None -def parse_can_buffer(dat): - ret = [] - for j in range(0, len(dat), 0x10): - ddat = dat[j:j + 0x10] - f1, f2 = struct.unpack("II", ddat[0:8]) - extended = 4 - if f1 & extended: - address = f1 >> 3 - else: - address = f1 >> 21 - dddat = ddat[8:8 + (f2 & 0xF)] +CANPACKET_HEAD_SIZE = 0x5 +DLC_TO_LEN = [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64] +LEN_TO_DLC = {length: dlc for (dlc, length) in enumerate(DLC_TO_LEN)} + +def pack_can_buffer(arr): + snds = [b''] + idx = 0 + for address, _, dat, bus in arr: + assert len(dat) in LEN_TO_DLC if DEBUG: - print(f" R 0x{address:x}: 0x{dddat.hex()}") - ret.append((address, f2 >> 16, dddat, (f2 >> 4) & 0xFF)) + print(f" W 0x{address:x}: 0x{dat.hex()}") + extended = 1 if address >= 0x800 else 0 + data_len_code = LEN_TO_DLC[len(dat)] + header = bytearray(5) + word_4b = address << 3 | extended << 2 + header[0] = (data_len_code << 4) | (bus << 1) + header[1] = word_4b & 0xFF + header[2] = (word_4b >> 8) & 0xFF + header[3] = (word_4b >> 16) & 0xFF + header[4] = (word_4b >> 24) & 0xFF + snds[idx] += header + dat + if len(snds[idx]) > 256: # Limit chunks to 256 bytes + snds.append(b'') + idx += 1 + + #Apply counter to each 64 byte packet + for idx in range(len(snds)): + tx = b'' + counter = 0 + for i in range (0, len(snds[idx]), 63): + tx += bytes([counter]) + snds[idx][i:i+63] + counter += 1 + snds[idx] = tx + return snds + +def unpack_can_buffer(dat): + ret = [] + counter = 0 + tail = bytearray() + for i in range(0, len(dat), 64): + if counter != dat[i]: + print("CAN: LOST RECV PACKET COUNTER") + break + counter+=1 + chunk = tail + dat[i+1:i+64] + tail = bytearray() + pos = 0 + while pos>4)] + pckt_len = CANPACKET_HEAD_SIZE + data_len + if pckt_len <= len(chunk[pos:]): + header = chunk[pos:pos+CANPACKET_HEAD_SIZE] + if len(header) < 5: + print("CAN: MALFORMED USB RECV PACKET") + break + bus = (header[0] >> 1) & 0x7 + address = (header[4] << 24 | header[3] << 16 | header[2] << 8 | header[1]) >> 3 + returned = (header[1] >> 1) & 0x1 + rejected = header[1] & 0x1 + data = chunk[pos + CANPACKET_HEAD_SIZE:pos + CANPACKET_HEAD_SIZE + data_len] + if returned: + bus += 128 + if rejected: + bus += 192 + if DEBUG: + print(f" R 0x{address:x}: 0x{data.hex()}") + ret.append((address, 0, data, bus)) + pos += pckt_len + else: + tail = chunk[pos:] + break return ret def ensure_health_packet_version(fn): @@ -76,7 +133,7 @@ class PandaWifiStreaming(object): try: dat, addr = self.sock.recvfrom(0x200 * 0x10) if addr == (self.ip, self.port): - ret += parse_can_buffer(dat) + ret += unpack_can_buffer(dat) except socket.error as e: if e.errno != 35 and e.errno != 11: traceback.print_exc() @@ -161,7 +218,7 @@ class Panda(object): HW_TYPE_DOS = b'\x06' HW_TYPE_RED_PANDA = b'\x07' - CAN_PACKET_VERSION = 1 + CAN_PACKET_VERSION = 2 HEALTH_PACKET_VERSION = 1 F2_DEVICES = [HW_TYPE_PEDAL] @@ -546,34 +603,20 @@ class Panda(object): @ensure_can_packet_version def can_send_many(self, arr, timeout=CAN_SEND_TIMEOUT_MS): - snds = [] - transmit = 1 - extended = 4 - for addr, _, dat, bus in arr: - assert len(dat) <= 8 - if DEBUG: - print(f" W 0x{addr:x}: 0x{dat.hex()}") - if addr >= 0x800: - rir = (addr << 3) | transmit | extended - else: - rir = (addr << 21) | transmit - snd = struct.pack("II", rir, len(dat) | (bus << 4)) + dat - snd = snd.ljust(0x10, b'\x00') - snds.append(snd) - + snds = pack_can_buffer(arr) while True: try: if self.wifi: for s in snds: self._handle.bulkWrite(3, s) else: - dat = b''.join(snds) - while True: - bs = self._handle.bulkWrite(3, dat, timeout=timeout) - dat = dat[bs:] - if len(dat) == 0: - break - print("CAN: PARTIAL SEND MANY, RETRYING") + for tx in snds: + while True: + bs = self._handle.bulkWrite(3, tx, timeout=timeout) + tx = tx[bs:] + if len(tx) == 0: + break + print("CAN: PARTIAL SEND MANY, RETRYING") break except (usb1.USBErrorIO, usb1.USBErrorOverflow): print("CAN: BAD SEND MANY, RETRYING") @@ -586,12 +629,12 @@ class Panda(object): dat = bytearray() while True: try: - dat = self._handle.bulkRead(1, 0x10 * 256) + dat = self._handle.bulkRead(1, 16384) # Max receive batch size + 2 extra reserve frames break except (usb1.USBErrorIO, usb1.USBErrorOverflow): print("CAN: BAD RECV, RETRYING") time.sleep(0.1) - return parse_can_buffer(dat) + return unpack_can_buffer(dat) def can_clear(self, bus): """Clears all messages from the specified internal CAN ringbuffer as diff --git a/tests/gmbitbang/test_packer.c b/tests/gmbitbang/test_packer.c index f056dd48..63c01310 100644 --- a/tests/gmbitbang/test_packer.c +++ b/tests/gmbitbang/test_packer.c @@ -1,21 +1,17 @@ #include #include -typedef struct { - uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ - uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ - uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ - uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ -} CAN_FIFOMailBox_TypeDef; +#define CANPACKET_DATA_SIZE_MAX 8 +#include "../../board/can_definitions.h" #include "../../board/drivers/canbitbang.h" int main() { char out[300]; - CAN_FIFOMailBox_TypeDef to_bang = {0}; - to_bang.RIR = 20 << 21; - to_bang.RDTR = 1; - to_bang.RDLR = 1; + CANPacket_t to_bang = {0}; + to_bang.addr = 20 << 18; + to_bang.data_len_code = 1; + to_bang.data[0] = 1; int len = get_bit_message(out, &to_bang); printf("T:"); diff --git a/tests/misra/suppressions.txt b/tests/misra/suppressions.txt index 44e59ace..f34a35ae 100644 --- a/tests/misra/suppressions.txt +++ b/tests/misra/suppressions.txt @@ -9,4 +9,8 @@ misra-c2012-19.2 # Advisory: The # and ## preprocessor operators should not be used misra-c2012-20.10 # Required: it's ok re-defining potentially reserved Macro names. Not likely to cause confusion -misra-c2012-21.1 \ No newline at end of file +misra-c2012-21.1 +# MUST BE FIXED, mostly in the safety code +misra-c2012-10.4 +# MUST BE FIXED, mostly in the safety code +misra-c2012-10.1 diff --git a/tests/safety/common.py b/tests/safety/common.py index 6bda0d38..7335f1e1 100644 --- a/tests/safety/common.py +++ b/tests/safety/common.py @@ -1,11 +1,11 @@ import os import abc -import struct import unittest import importlib import numpy as np from typing import Optional, List, Dict from opendbc.can.packer import CANPacker # pylint: disable=import-error +from panda import LEN_TO_DLC from panda.tests.safety import libpandasafety_py MAX_WRONG_COUNTERS = 5 @@ -18,16 +18,12 @@ class UNSAFE_MODE: def package_can_msg(msg): addr, _, dat, bus = msg - rdlr, rdhr = struct.unpack('II', dat.ljust(8, b'\x00')) - - ret = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - if addr >= 0x800: - ret[0].RIR = (addr << 3) | 5 - else: - ret[0].RIR = (addr << 21) | 1 - ret[0].RDTR = len(dat) | ((bus & 0xF) << 4) - ret[0].RDHR = rdhr - ret[0].RDLR = rdlr + ret = libpandasafety_py.ffi.new('CANPacket_t *') + ret[0].extended = 1 if addr >= 0x800 else 0 + ret[0].addr = addr + ret[0].data_len_code = LEN_TO_DLC[len(dat)] + ret[0].bus = bus + ret[0].data = bytes(dat) return ret diff --git a/tests/safety/libpandasafety_py.py b/tests/safety/libpandasafety_py.py index c0099818..12d2c6cb 100644 --- a/tests/safety/libpandasafety_py.py +++ b/tests/safety/libpandasafety_py.py @@ -7,22 +7,19 @@ libpandasafety_fn = os.path.join(can_dir, "libpandasafety.so") ffi = FFI() ffi.cdef(""" -typedef struct -{ - uint32_t TIR; /*!< CAN TX mailbox identifier register */ - uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */ - uint32_t TDLR; /*!< CAN mailbox data low register */ - uint32_t TDHR; /*!< CAN mailbox data high register */ -} CAN_TxMailBox_TypeDef; - -typedef struct -{ - uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ - uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ - uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ - uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ -} CAN_FIFOMailBox_TypeDef; +typedef struct { + unsigned char reserved : 1; + unsigned char bus : 3; + unsigned char data_len_code : 4; + unsigned char rejected : 1; + unsigned char returned : 1; + unsigned char extended : 1; + unsigned int addr : 29; + unsigned char data[8]; +} CANPacket_t; +""", packed=True) +ffi.cdef(""" typedef struct { uint32_t CNT; @@ -55,9 +52,9 @@ bool get_vehicle_moving(void); int get_hw_type(void); void set_timer(uint32_t t); -int safety_rx_hook(CAN_FIFOMailBox_TypeDef *to_send); -int safety_tx_hook(CAN_FIFOMailBox_TypeDef *to_push); -int safety_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd); +int safety_rx_hook(CANPacket_t *to_send); +int safety_tx_hook(CANPacket_t *to_push); +int safety_fwd_hook(int bus_num, CANPacket_t *to_fwd); int set_safety_hooks(uint16_t mode, int16_t param); void init_tests(void); diff --git a/tests/safety/test.c b/tests/safety/test.c index 5cf1b7f5..015d179f 100644 --- a/tests/safety/test.c +++ b/tests/safety/test.c @@ -2,21 +2,8 @@ #include #include -typedef struct -{ - uint32_t TIR; /*!< CAN TX mailbox identifier register */ - uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */ - uint32_t TDLR; /*!< CAN mailbox data low register */ - uint32_t TDHR; /*!< CAN mailbox data high register */ -} CAN_TxMailBox_TypeDef; - -typedef struct -{ - uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ - uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ - uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ - uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ -} CAN_FIFOMailBox_TypeDef; +#define CANPACKET_DATA_SIZE_MAX 8 +#include "can_definitions.h" typedef struct { @@ -66,15 +53,6 @@ void fault_occurred(uint32_t fault) { void fault_recovered(uint32_t fault) { } -// from llcan.h -#define GET_BUS(msg) (((msg)->RDTR >> 4) & 0xFF) -#define GET_LEN(msg) ((msg)->RDTR & 0xf) -#define GET_ADDR(msg) ((((msg)->RIR & 4) != 0) ? ((msg)->RIR >> 3) : ((msg)->RIR >> 21)) -#define GET_BYTE(msg, b) (((int)(b) > 3) ? (((msg)->RDHR >> (8U * ((unsigned int)(b) % 4U))) & 0XFFU) : (((msg)->RDLR >> (8U * (unsigned int)(b))) & 0xFFU)) -#define GET_BYTES_04(msg) ((msg)->RDLR) -#define GET_BYTES_48(msg) ((msg)->RDHR) -#define GET_FLAG(value, mask) (((__typeof__(mask))param & mask) == mask) - #define UNUSED(x) (void)(x) #ifndef PANDA diff --git a/tests/safety/test_honda.py b/tests/safety/test_honda.py index 276021ca..1f8152e3 100755 --- a/tests/safety/test_honda.py +++ b/tests/safety/test_honda.py @@ -116,7 +116,10 @@ class TestHondaSafety(common.PandaSafetyTest): to_push = self._speed_msg(0) self.assertTrue(self._rx(to_push)) if msg != "btn": - to_push[0].RDHR = 0 # invalidate checksum + to_push[0].data[4] = 0 # invalidate checksum + to_push[0].data[5] = 0 + to_push[0].data[6] = 0 + to_push[0].data[7] = 0 self.assertFalse(self._rx(to_push)) self.assertFalse(self.safety.get_controls_allowed()) @@ -203,8 +206,10 @@ class TestHondaNidecSafety(TestHondaSafety, common.InterceptorSafetyTest): def _interceptor_msg(self, gas, addr): to_send = make_msg(0, addr, 6) gas2 = gas * 2 - to_send[0].RDLR = ((gas & 0xff) << 8) | ((gas & 0xff00) >> 8) | \ - ((gas2 & 0xff) << 24) | ((gas2 & 0xff00) << 8) + to_send[0].data[0] = (gas & 0xFF00) >> 8 + to_send[0].data[1] = gas & 0xFF + to_send[0].data[2] = (gas2 & 0xFF00) >> 8 + to_send[0].data[3] = gas2 & 0xFF return to_send def _send_brake_msg(self, brake): diff --git a/tests/safety/test_toyota.py b/tests/safety/test_toyota.py index ee1d1dea..11c0a5fb 100755 --- a/tests/safety/test_toyota.py +++ b/tests/safety/test_toyota.py @@ -74,8 +74,10 @@ class TestToyotaSafety(common.PandaSafetyTest, common.InterceptorSafetyTest, # Toyota gas gains are the same def _interceptor_msg(self, gas, addr): to_send = make_msg(0, addr, 6) - to_send[0].RDLR = ((gas & 0xff) << 8) | ((gas & 0xff00) >> 8) | \ - ((gas & 0xff) << 24) | ((gas & 0xff00) << 8) + to_send[0].data[0] = (gas & 0xFF00) >> 8 + to_send[0].data[1] = gas & 0xFF + to_send[0].data[2] = (gas & 0xFF00) >> 8 + to_send[0].data[3] = gas & 0xFF return to_send def test_accel_actuation_limits(self): @@ -122,7 +124,10 @@ class TestToyotaSafety(common.PandaSafetyTest, common.InterceptorSafetyTest, if msg == "pcm": to_push = self._pcm_status_msg(True) self.assertTrue(self._rx(to_push)) - to_push[0].RDHR = 0 + to_push[0].data[4] = 0 + to_push[0].data[5] = 0 + to_push[0].data[6] = 0 + to_push[0].data[7] = 0 self.assertFalse(self._rx(to_push)) self.assertFalse(self.safety.get_controls_allowed()) diff --git a/tests/safety/test_volkswagen_mqb.py b/tests/safety/test_volkswagen_mqb.py index 68760ff4..8b1c604f 100644 --- a/tests/safety/test_volkswagen_mqb.py +++ b/tests/safety/test_volkswagen_mqb.py @@ -225,7 +225,7 @@ class TestVolkswagenMqbSafety(common.PandaSafetyTest): if msg == MSG_MOTOR_20: to_push = self._gas_msg(0) self.assertTrue(self._rx(to_push)) - to_push[0].RDHR ^= 0xFF + to_push[0].data[4] ^= 0xFF self.assertFalse(self._rx(to_push)) self.assertFalse(self.safety.get_controls_allowed()) diff --git a/tests/safety/test_volkswagen_pq.py b/tests/safety/test_volkswagen_pq.py index 9d9f7e06..87f00f67 100644 --- a/tests/safety/test_volkswagen_pq.py +++ b/tests/safety/test_volkswagen_pq.py @@ -25,7 +25,7 @@ MSG_LDW_1 = 0x5BE # TX by OP, Lane line recognition and text alerts def volkswagen_pq_checksum(msg, addr, len_msg): - msg_bytes = msg.RDLR.to_bytes(4, 'little') + msg.RDHR.to_bytes(4, 'little') + msg_bytes = bytes(msg.data) msg_bytes = msg_bytes[1:len_msg] checksum = 0 @@ -234,7 +234,7 @@ class TestVolkswagenPqSafety(common.PandaSafetyTest): self.safety.set_controls_allowed(1) to_push = self._lenkhilfe_3_msg(0) self.assertTrue(self._rx(to_push)) - to_push[0].RDHR ^= 0xFF + to_push[0].data[4] ^= 0xFF self.assertFalse(self._rx(to_push)) self.assertFalse(self.safety.get_controls_allowed()) diff --git a/tests/safety_replay/helpers.py b/tests/safety_replay/helpers.py index 572ab7d3..e3ed215a 100644 --- a/tests/safety_replay/helpers.py +++ b/tests/safety_replay/helpers.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -import struct import panda.tests.safety.libpandasafety_py as libpandasafety_py -from panda import Panda +from panda import Panda, LEN_TO_DLC def to_signed(d, bits): ret = d @@ -59,16 +58,12 @@ def set_desired_torque_last(safety, mode, torque): safety.set_subaru_desired_torque_last(torque) def package_can_msg(msg): - rdlr, rdhr = struct.unpack('II', msg.dat.ljust(8, b'\x00')) - - ret = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') - if msg.address >= 0x800: - ret[0].RIR = (msg.address << 3) | 5 - else: - ret[0].RIR = (msg.address << 21) | 1 - ret[0].RDTR = len(msg.dat) | ((msg.src & 0xF) << 4) - ret[0].RDHR = rdhr - ret[0].RDLR = rdlr + ret = libpandasafety_py.ffi.new('CANPacket_t *') + ret[0].extended = 1 if msg.address >= 0x800 else 0 + ret[0].addr = msg.address + ret[0].data_len_code = LEN_TO_DLC[len(msg.dat)] + ret[0].bus = msg.src + ret[0].data = msg.dat return ret diff --git a/tests/usbprotocol/test_pandalib.py b/tests/usbprotocol/test_pandalib.py new file mode 100644 index 00000000..7f2a3b38 --- /dev/null +++ b/tests/usbprotocol/test_pandalib.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +import random +import unittest +from panda import pack_can_buffer, unpack_can_buffer + + +class PandaTestPackUnpack(unittest.TestCase): + def test_panda_lib_pack_unpack(self): + to_pack = [] + for _ in range(10000): + address = random.randint(1, 0x1FFFFFFF) + data = bytes([random.getrandbits(8) for _ in range(random.randrange(1, 9))]) + to_pack.append((address, 0, data, 0)) + + packed = pack_can_buffer(to_pack) + unpacked = [] + for dat in packed: + unpacked.extend(unpack_can_buffer(dat)) + + assert unpacked == to_pack + +if __name__ == "__main__": + unittest.main()