diff --git a/Dockerfile b/Dockerfile index 670283dd47..8120b48257 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,9 +37,9 @@ RUN pip3 install --break-system-packages --no-cache-dir $PYTHONPATH/panda/[dev] # TODO: this should be a "pip install" or not even in this repo at all RUN git config --global --add safe.directory $PYTHONPATH/panda -ENV OPENDBC_REF="5ed7a834a4e0e24c3968dd1e98ceb4b9d5f9791a" +ENV OPENDBC_REF="7099534b54cd3527fbfd5a45fe7067652fa1ee7b" RUN cd /tmp/ && \ - git clone --depth 1 https://github.com/commaai/opendbc opendbc_repo && \ + git clone --depth 1 https://github.com/sunnypilot/opendbc opendbc_repo && \ cd opendbc_repo && git fetch origin $OPENDBC_REF && git checkout FETCH_HEAD && rm -rf .git/ && \ pip3 install --break-system-packages --no-cache-dir Cython numpy && \ scons -j8 --minimal opendbc/ && \ diff --git a/board/safety/safety_hyundai.h b/board/safety/safety_hyundai.h index 3e9d23216c..479b03875c 100644 --- a/board/safety/safety_hyundai.h +++ b/board/safety/safety_hyundai.h @@ -3,13 +3,13 @@ #include "safety_declarations.h" #include "safety_hyundai_common.h" -#define HYUNDAI_LIMITS(steer, rate_up, rate_down) { \ +#define HYUNDAI_LIMITS(steer, rate_up, rate_down, drv_trq_alloweance) { \ .max_steer = (steer), \ .max_rate_up = (rate_up), \ .max_rate_down = (rate_down), \ .max_rt_delta = 112, \ .max_rt_interval = 250000, \ - .driver_torque_allowance = 50, \ + .driver_torque_allowance = (drv_trq_alloweance), \ .driver_torque_factor = 2, \ .type = TorqueDriverLimited, \ /* the EPS faults when the steering angle is above a certain threshold for too long. to prevent this, */ \ @@ -32,14 +32,14 @@ static const CanMsg HYUNDAI_TX_MSGS[] = { {0x485, 0, 4}, // LFAHDA_MFC Bus 0 }; -#define HYUNDAI_COMMON_RX_CHECKS(legacy) \ - {.msg = {{0x260, 0, 8, .check_checksum = true, .max_counter = 3U, .frequency = 100U}, \ - {0x371, 0, 8, .frequency = 100U}, { 0 }}}, \ - {.msg = {{0x386, 0, 8, .check_checksum = !(legacy), .max_counter = (legacy) ? 0U : 15U, .frequency = 100U}, { 0 }, { 0 }}}, \ - {.msg = {{0x394, 0, 8, .check_checksum = !(legacy), .max_counter = (legacy) ? 0U : 7U, .frequency = 100U}, { 0 }, { 0 }}}, \ +#define HYUNDAI_COMMON_RX_CHECKS(pt_bus, can_canfd_hybrid, legacy) \ + {.msg = {{0x260, pt_bus, 8, .check_checksum = true, .max_counter = 3U, .frequency = 100U}, \ + {0x371, 0, 8, .frequency = 100U}, { 0 }}}, \ + {.msg = {{0x386, pt_bus, 8, .check_checksum = !(legacy), .max_counter = (legacy) ? 0U : 15U, .frequency = (can_canfd_hybrid) ? 50U : 100U}, { 0 }, { 0 }}}, \ + {.msg = {{0x394, pt_bus, 8, .check_checksum = !(legacy), .max_counter = (legacy) ? 0U : 7U, .frequency = (can_canfd_hybrid) ? 50U : 100U}, { 0 }, { 0 }}}, \ -#define HYUNDAI_SCC12_ADDR_CHECK(scc_bus) \ - {.msg = {{0x421, (scc_bus), 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \ +#define HYUNDAI_SCC12_ADDR_CHECK(scc_bus, can_canfd_hybrid) \ + {.msg = {{0x421, (scc_bus), 8, .check_checksum = !(can_canfd_hybrid), .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \ static bool hyundai_legacy = false; @@ -54,7 +54,8 @@ static uint8_t hyundai_get_counter(const CANPacket_t *to_push) { } else if (addr == 0x394) { cnt = (GET_BYTE(to_push, 1) >> 5) & 0x7U; } else if (addr == 0x421) { - cnt = GET_BYTE(to_push, 7) & 0xFU; + uint8_t byte_421 = hyundai_can_canfd_hybrid ? (GET_BYTE(to_push, 1) >> 4) : GET_BYTE(to_push, 7); + cnt = byte_421 & 0xFU; } else if (addr == 0x4F1) { cnt = (GET_BYTE(to_push, 3) >> 4) & 0xFU; } else { @@ -73,7 +74,7 @@ static uint32_t hyundai_get_checksum(const CANPacket_t *to_push) { } else if (addr == 0x394) { chksum = GET_BYTE(to_push, 6) & 0xFU; } else if (addr == 0x421) { - chksum = GET_BYTE(to_push, 7) >> 4; + chksum = hyundai_can_canfd_hybrid ? GET_BYTE(to_push, 0) : GET_BYTE(to_push, 7) >> 4; } else { } return chksum; @@ -109,7 +110,7 @@ static uint32_t hyundai_compute_checksum(const CANPacket_t *to_push) { } chksum += (b % 16U) + (b / 16U); } - chksum = (16U - (chksum % 16U)) % 16U; + chksum = (16U - (chksum % 16U)) % 16U; } return chksum; @@ -119,14 +120,18 @@ static void hyundai_rx_hook(const CANPacket_t *to_push) { int bus = GET_BUS(to_push); int addr = GET_ADDR(to_push); + const int pt_bus = hyundai_can_canfd_hybrid ? 1 : 0; + const int non_cam_scc_bus = hyundai_can_canfd_hybrid ? 1 : 0; + const int scc_bus = hyundai_camera_scc ? 2 : non_cam_scc_bus; + // SCC12 is on bus 2 for camera-based SCC cars, bus 0 on all others - if ((addr == 0x421) && (((bus == 0) && !hyundai_camera_scc) || ((bus == 2) && hyundai_camera_scc))) { - // 2 bits: 13-14 - int cruise_engaged = (GET_BYTES(to_push, 0, 4) >> 13) & 0x3U; + if ((addr == 0x421) && (bus == scc_bus)) { + uint8_t cruise_byte = hyundai_can_canfd_hybrid ? (GET_BYTE(to_push, 3) >> 4) : (GET_BYTES(to_push, 0, 4) >> 13); + bool cruise_engaged = (cruise_byte & 0x3U) != 0U; hyundai_common_cruise_state_check(cruise_engaged); } - if (bus == 0) { + if (bus == pt_bus) { if (addr == 0x251) { int torque_driver_new = (GET_BYTES(to_push, 0, 2) & 0x7ffU) - 1024U; // update array of samples @@ -160,21 +165,23 @@ static void hyundai_rx_hook(const CANPacket_t *to_push) { if (addr == 0x394) { brake_pressed = ((GET_BYTE(to_push, 5) >> 5U) & 0x3U) == 0x2U; } + } - bool stock_ecu_detected = (addr == 0x340); + const int steer_addr = hyundai_canfd_hda2 ? 0x50 : 0x340; + bool stock_ecu_detected = (addr == steer_addr) && (bus == 0); - // If openpilot is controlling longitudinal we need to ensure the radar is turned off - // Enforce by checking we don't see SCC12 - if (hyundai_longitudinal && (addr == 0x421)) { - stock_ecu_detected = true; - } - generic_rx_checks(stock_ecu_detected); + // If openpilot is controlling longitudinal we need to ensure the radar is turned off + // Enforce by checking we don't see SCC12 + if (hyundai_longitudinal) { + stock_ecu_detected = stock_ecu_detected || ((addr == 0x421) && (bus == 0)); } + generic_rx_checks(stock_ecu_detected); } static bool hyundai_tx_hook(const CANPacket_t *to_send) { - const SteeringLimits HYUNDAI_STEERING_LIMITS = HYUNDAI_LIMITS(384, 3, 7); - const SteeringLimits HYUNDAI_STEERING_LIMITS_ALT = HYUNDAI_LIMITS(270, 2, 3); + const SteeringLimits HYUNDAI_STEERING_LIMITS = HYUNDAI_LIMITS(384, 3, 7, 50); + const SteeringLimits HYUNDAI_STEERING_LIMITS_ALT = HYUNDAI_LIMITS(270, 2, 3, 50); + const SteeringLimits HYUNDAI_STEERING_LIMITS_CAN_CANFD_HYBRID = HYUNDAI_LIMITS(384, 2, 3, 250); bool tx = true; int addr = GET_ADDR(to_send); @@ -221,6 +228,16 @@ static bool hyundai_tx_hook(const CANPacket_t *to_send) { } } + // CAN CAN-FD Hybrid steering + if (addr == 0x50) { + int desired_torque = (((GET_BYTE(to_send, 6) & 0xFU) << 7U) | (GET_BYTE(to_send, 5) >> 1U)) - 1024U; + bool steer_req = GET_BIT(to_send, 52U); + + if (steer_torque_cmd_checks(desired_torque, steer_req, HYUNDAI_STEERING_LIMITS_CAN_CANFD_HYBRID)) { + tx = false; + } + } + // UDS: Only tester present ("\x02\x3E\x80\x00\x00\x00\x00\x00") allowed on diagnostics address if (addr == 0x7D0) { if ((GET_BYTES(to_send, 0, 4) != 0x00803E02U) || (GET_BYTES(to_send, 4, 4) != 0x0U)) { @@ -250,8 +267,18 @@ static int hyundai_fwd_hook(int bus_num, int addr) { if (bus_num == 0) { bus_fwd = 2; } - if ((bus_num == 2) && (addr != 0x340) && (addr != 0x485)) { - bus_fwd = 0; + if (bus_num == 2) { + // LKAS11 for CAN, LKAS for CAN/CAN-FD + bool is_lkas11_msg = (addr == 0x340) && !hyundai_canfd_hda2; + bool is_lkas_msg = ((addr == 0x50) || (addr == 0x2a4)) && hyundai_canfd_hda2; + + // LFAHDA_MFC for CAN + bool is_lfahda_msg = (addr == 0x485) && !hyundai_canfd_hda2; + + bool block_msg = is_lkas11_msg || is_lkas_msg || is_lfahda_msg; + if (!block_msg) { + bus_fwd = 0; + } } return bus_fwd; @@ -278,17 +305,23 @@ static safety_config hyundai_init(uint16_t param) { {0x485, 0, 4}, // LFAHDA_MFC Bus 0 }; + static const CanMsg HYUNDAI_CAN_CANFD_HYBRID_HDA2_TX_MSGS[] = { + {0x50, 0, 16}, + {0x4F1, 1, 4}, + {0x2A4, 0, 24}, + }; + hyundai_common_init(param); hyundai_legacy = false; - if (hyundai_camera_scc) { + if (hyundai_camera_scc || hyundai_can_canfd_hybrid) { hyundai_longitudinal = false; } safety_config ret; if (hyundai_longitudinal) { static RxCheck hyundai_long_rx_checks[] = { - HYUNDAI_COMMON_RX_CHECKS(false) + HYUNDAI_COMMON_RX_CHECKS(0, false, false) // Use CLU11 (buttons) to manage controls allowed instead of SCC cruise state {.msg = {{0x4F1, 0, 4, .check_checksum = false, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, }; @@ -296,15 +329,22 @@ static safety_config hyundai_init(uint16_t param) { ret = BUILD_SAFETY_CFG(hyundai_long_rx_checks, HYUNDAI_LONG_TX_MSGS); } else if (hyundai_camera_scc) { static RxCheck hyundai_cam_scc_rx_checks[] = { - HYUNDAI_COMMON_RX_CHECKS(false) - HYUNDAI_SCC12_ADDR_CHECK(2) + HYUNDAI_COMMON_RX_CHECKS(0, false, false) + HYUNDAI_SCC12_ADDR_CHECK(2, false) }; ret = BUILD_SAFETY_CFG(hyundai_cam_scc_rx_checks, HYUNDAI_CAMERA_SCC_TX_MSGS); + } else if (hyundai_can_canfd_hybrid) { + static RxCheck hyundai_can_canfd_hda2_rx_checks[] = { + HYUNDAI_COMMON_RX_CHECKS(1, true, false) + HYUNDAI_SCC12_ADDR_CHECK(1, true) + }; + + ret = BUILD_SAFETY_CFG(hyundai_can_canfd_hda2_rx_checks, HYUNDAI_CAN_CANFD_HYBRID_HDA2_TX_MSGS); } else { static RxCheck hyundai_rx_checks[] = { - HYUNDAI_COMMON_RX_CHECKS(false) - HYUNDAI_SCC12_ADDR_CHECK(0) + HYUNDAI_COMMON_RX_CHECKS(0, false, false) + HYUNDAI_SCC12_ADDR_CHECK(0, false) }; ret = BUILD_SAFETY_CFG(hyundai_rx_checks, HYUNDAI_TX_MSGS); @@ -315,8 +355,8 @@ static safety_config hyundai_init(uint16_t param) { static safety_config hyundai_legacy_init(uint16_t param) { // older hyundai models have less checks due to missing counters and checksums static RxCheck hyundai_legacy_rx_checks[] = { - HYUNDAI_COMMON_RX_CHECKS(true) - HYUNDAI_SCC12_ADDR_CHECK(0) + HYUNDAI_COMMON_RX_CHECKS(0, false, true) + HYUNDAI_SCC12_ADDR_CHECK(0, false) }; hyundai_common_init(param); diff --git a/board/safety/safety_hyundai_common.h b/board/safety/safety_hyundai_common.h index d83b396401..e6ad070f43 100644 --- a/board/safety/safety_hyundai_common.h +++ b/board/safety/safety_hyundai_common.h @@ -36,6 +36,9 @@ bool hyundai_canfd_hda2 = false; extern bool hyundai_alt_limits; bool hyundai_alt_limits = false; +extern bool hyundai_can_canfd_hybrid; +bool hyundai_can_canfd_hybrid = false; + static uint8_t hyundai_last_button_interaction; // button messages since the user pressed an enable button void hyundai_common_init(uint16_t param) { @@ -44,12 +47,14 @@ void hyundai_common_init(uint16_t param) { const int HYUNDAI_PARAM_CAMERA_SCC = 8; const int HYUNDAI_PARAM_CANFD_HDA2 = 16; const int HYUNDAI_PARAM_ALT_LIMITS = 64; // TODO: shift this down with the rest of the common flags + const int HYUNDAI_PARAM_CAN_CANFD_HYBRID = 256; hyundai_ev_gas_signal = GET_FLAG(param, HYUNDAI_PARAM_EV_GAS); hyundai_hybrid_gas_signal = !hyundai_ev_gas_signal && GET_FLAG(param, HYUNDAI_PARAM_HYBRID_GAS); hyundai_camera_scc = GET_FLAG(param, HYUNDAI_PARAM_CAMERA_SCC); hyundai_canfd_hda2 = GET_FLAG(param, HYUNDAI_PARAM_CANFD_HDA2); hyundai_alt_limits = GET_FLAG(param, HYUNDAI_PARAM_ALT_LIMITS); + hyundai_can_canfd_hybrid = GET_FLAG(param, HYUNDAI_PARAM_CAN_CANFD_HYBRID); hyundai_last_button_interaction = HYUNDAI_PREV_BUTTON_SAMPLES; diff --git a/python/__init__.py b/python/__init__.py index d2bbb2fe45..b3f1a784b3 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -201,6 +201,7 @@ class Panda: FLAG_HYUNDAI_CANFD_ALT_BUTTONS = 32 FLAG_HYUNDAI_ALT_LIMITS = 64 FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING = 128 + FLAG_HYUNDAI_CAN_CANFD_HYBRID = 256 FLAG_TESLA_POWERTRAIN = 1 FLAG_TESLA_LONG_CONTROL = 2 diff --git a/tests/safety/test_hyundai.py b/tests/safety/test_hyundai.py index 1bff99fa75..938522ed88 100755 --- a/tests/safety/test_hyundai.py +++ b/tests/safety/test_hyundai.py @@ -63,6 +63,9 @@ class TestHyundaiSafety(HyundaiButtonBase, common.PandaCarSafetyTest, common.Dri MAX_INVALID_STEERING_FRAMES = 2 MIN_VALID_STEERING_RT_INTERVAL = 810000 # a ~10% buffer, can send steer up to 110Hz + PT_BUS = 0 + STEER_BUS = 0 + cnt_gas = 0 cnt_speed = 0 cnt_brake = 0 @@ -75,7 +78,9 @@ def setUp(self): self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI, 0) self.safety.init_tests() - def _button_msg(self, buttons, main_button=0, bus=0): + def _button_msg(self, buttons, main_button=0, bus=None): + if bus is None: + bus = self.PT_BUS values = {"CF_Clu_CruiseSwState": buttons, "CF_Clu_CruiseSwMain": main_button, "CF_Clu_AliveCnt1": self.cnt_button} self.__class__.cnt_button += 1 return self.packer.make_can_msg_panda("CLU11", bus, values) @@ -83,13 +88,13 @@ def _button_msg(self, buttons, main_button=0, bus=0): def _user_gas_msg(self, gas): values = {"CF_Ems_AclAct": gas, "AliveCounter": self.cnt_gas % 4} self.__class__.cnt_gas += 1 - return self.packer.make_can_msg_panda("EMS16", 0, values, fix_checksum=checksum) + return self.packer.make_can_msg_panda("EMS16", self.PT_BUS, values, fix_checksum=checksum) def _user_brake_msg(self, brake): values = {"DriverOverride": 2 if brake else random.choice((0, 1, 3)), "AliveCounterTCS": self.cnt_brake % 8} self.__class__.cnt_brake += 1 - return self.packer.make_can_msg_panda("TCS13", 0, values, fix_checksum=checksum) + return self.packer.make_can_msg_panda("TCS13", self.PT_BUS, values, fix_checksum=checksum) def _speed_msg(self, speed): # panda safety doesn't scale, so undo the scaling @@ -97,7 +102,7 @@ def _speed_msg(self, speed): values["WHL_SPD_AliveCounter_LSB"] = (self.cnt_speed % 16) & 0x3 values["WHL_SPD_AliveCounter_MSB"] = (self.cnt_speed % 16) >> 2 self.__class__.cnt_speed += 1 - return self.packer.make_can_msg_panda("WHL_SPD11", 0, values, fix_checksum=checksum) + return self.packer.make_can_msg_panda("WHL_SPD11", self.PT_BUS, values, fix_checksum=checksum) def _pcm_status_msg(self, enable): values = {"ACCMode": enable, "CR_VSM_Alive": self.cnt_cruise % 16} @@ -106,11 +111,11 @@ def _pcm_status_msg(self, enable): def _torque_driver_msg(self, torque): values = {"CR_Mdps_StrColTq": torque} - return self.packer.make_can_msg_panda("MDPS12", 0, values) + return self.packer.make_can_msg_panda("MDPS12", self.PT_BUS, values) def _torque_cmd_msg(self, torque, steer_req=1): values = {"CR_Lkas_StrToqReq": torque, "CF_Lkas_ActToi": steer_req} - return self.packer.make_can_msg_panda("LKAS11", 0, values) + return self.packer.make_can_msg_panda("LKAS11", self.STEER_BUS, values) class TestHyundaiSafetyAltLimits(TestHyundaiSafety): @@ -167,6 +172,36 @@ def _user_gas_msg(self, gas): values = {"CR_Vcu_AccPedDep_Pos": gas} return self.packer.make_can_msg_panda("E_EMS11", 0, values, fix_checksum=checksum) + +class TestHyundaiCanCanfdHybridHda2Safety(TestHyundaiSafety): + TX_MSGS = [[0x50, 0], [0x4F1, 1], [0x2A4, 0]] + RELAY_MALFUNCTION_ADDRS = {0: (0x50, )} # LKAS + FWD_BLACKLISTED_ADDRS = {2: [0x50, 0x2A4]} # LKAS, CAM_0x2a4 + + MAX_RATE_UP = 2 + MAX_RATE_DOWN = 3 + DRIVER_TORQUE_ALLOWANCE = 250 + + PT_BUS = 1 + BUTTONS_TX_BUS = 1 + SCC_BUS = 1 + + def setUp(self): + self.packer = CANPackerPanda("hyundai_palisade_2023_generated") + self.safety = libpanda_py.libpanda + self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI, Panda.FLAG_HYUNDAI_CAN_CANFD_HYBRID | Panda.FLAG_HYUNDAI_CANFD_HDA2) + self.safety.init_tests() + + def _torque_cmd_msg(self, torque, steer_req=1): + values = {"TORQUE_REQUEST": torque, "STEER_REQ": steer_req} + return self.packer.make_can_msg_panda("LKAS", self.STEER_BUS, values) + + def _pcm_status_msg(self, enable): + values = {"ACCMode": enable, "CR_VSM_Alive": self.cnt_cruise % 16} + self.__class__.cnt_cruise += 1 + return self.packer.make_can_msg_panda("SCC12", self.SCC_BUS, values) + + class TestHyundaiLongitudinalSafety(HyundaiLongitudinalBase, TestHyundaiSafety): TX_MSGS = [[0x340, 0], [0x4F1, 0], [0x485, 0], [0x420, 0], [0x421, 0], [0x50A, 0], [0x389, 0], [0x4A2, 0], [0x38D, 0], [0x483, 0], [0x7D0, 0]]