IQ.Pilot Release Commit @ 6d177d4

This commit is contained in:
IQ.Lvbs CI [bot]
2026-07-22 23:21:13 -05:00
parent ff4dfcb728
commit 401cac1028
93 changed files with 9223 additions and 4600 deletions

View File

@@ -3,6 +3,8 @@
#include "iqdbc/safety/declarations.h"
#include "iqdbc/safety/modes/hyundai_common.h"
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#define HYUNDAI_LIMITS(steer, rate_up, rate_down) { \
.max_torque = (steer), \
.max_rate_up = (rate_up), \
@@ -21,96 +23,84 @@
extern const LongitudinalLimits HYUNDAI_LONG_LIMITS;
const LongitudinalLimits HYUNDAI_LONG_LIMITS = {
.max_accel = 200, // 1/100 m/s2
.min_accel = -350, // 1/100 m/s2
.zero_accel = 0,
.max_accel = 250, // 1/100 m/s2
.min_accel = -400, // 1/100 m/s2
};
#define HYUNDAI_COMMON_TX_MSGS(scc_bus) \
{0x340, 0, 8, .check_relay = true}, /* LKAS11 Bus 0 */ \
{0x4F1, scc_bus, 4, .check_relay = false}, /* CLU11 Bus 0 (radar-SCC) or 2 (camera-SCC) */ \
{0x485, 0, 4, .check_relay = true}, /* LFAHDA_MFC Bus 0 */ \
#define HYUNDAI_LONG_COMMON_TX_MSGS(scc_bus) \
HYUNDAI_COMMON_TX_MSGS(scc_bus) \
{0x420, 0, 8, .check_relay = true}, /* SCC11 Bus 0 */ \
{0x421, 0, 8, .check_relay = true}, /* SCC12 Bus 0 */ \
{0x50A, 0, 8, .check_relay = true}, /* SCC13 Bus 0 */ \
{0x389, 0, 8, .check_relay = true}, /* SCC14 Bus 0 */ \
{0x4A2, 0, 2, .check_relay = false}, /* FRT_RADAR11 Bus 0 */ \
#define HYUNDAI_COMMON_RX_CHECKS(legacy) \
{.msg = {{0x260, 0, 8, 100U, .max_counter = 3U, .ignore_quality_flag = true}, \
{0x371, 0, 8, 100U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }}}, \
{.msg = {{0x386, 0, 8, 100U, .ignore_checksum = (legacy), .ignore_counter = (legacy), .max_counter = (legacy) ? 0U : 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{0x394, 0, 8, 100U, .ignore_checksum = (legacy), .ignore_counter = (legacy), .max_counter = (legacy) ? 0U : 7U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{0x251, 0, 8, 50U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{0x4F1, 0, 4, 50U, .ignore_checksum = true, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
#define HYUNDAI_SCC11_ADDR_CHECK(scc_bus) \
{.msg = {{0x420, (scc_bus), 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true, .frequency = 50U}, { 0 }, { 0 }}}, \
#define HYUNDAI_SCC12_ADDR_CHECK(scc_bus) \
{.msg = {{0x421, (scc_bus), 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
#define HYUNDAI_FCEV_GAS_ADDR_CHECK \
{.msg = {{0x91, 0, 8, 100U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
#define HYUNDAI_LDA_BUTTON_ADDR_CHECK \
{.msg = {{0x391, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true, .frequency = 50U}, { 0 }, { 0 }}}, \
#define HYUNDAI_NON_SCC_HEV_ADDR_CHECK \
{.msg = {{0x595U, 0, 8, 10U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
#define HYUNDAI_NON_SCC_EV_ADDR_CHECK \
{.msg = {{0x592U, 0, 8, 10U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
static const CanMsg HYUNDAI_TX_MSGS[] = {
HYUNDAI_COMMON_TX_MSGS(0)
{0x340, 0, 8}, // LKAS11 Bus 0
{0x4F1, 0, 4}, // CLU11 Bus 0
{0x485, 0, 8}, // LFAHDA_MFC Bus 0
{593, 2, 8}, // MDPS12, Bus 2
{1056, 0, 8}, // SCC11, Bus 0
{1057, 0, 8}, // SCC12, Bus 0
{1290, 0, 8}, // SCC13, Bus 0
{905, 0, 8}, // SCC14, Bus 0
{909, 0, 8}, // FCA11 Bus 0
{1155, 0, 8}, // FCA12 Bus 0
{1186, 0, 8}, // FRT_RADAR11, Bus 0
{1265, 2, 4}, // CLU11, Bus 0, 2
{0x7D0, 0, 8}, // radar UDS TX addr Bus 0 (for radar disable) // 2000
{0x7b1, 0, 8},
};
static bool hyundai_legacy = false;
#define HYUNDAI_COMMON_RX_CHECKS(legacy) \
{.msg = {{0x260, 0, 8, .max_counter = 3U, .frequency = 100U}, \
{0x371, 0, 8, .ignore_checksum = true, .ignore_counter = true, .frequency = 100U}, \
{0x91, 0, 8, .ignore_checksum = true, .ignore_counter = true, .frequency = 100U}}}, \
{.msg = {{0x386, 0, 8, .ignore_checksum = (legacy), .ignore_counter = (legacy), .max_counter = (legacy) ? 0U : 15U, .frequency = 100U}, { 0 }, { 0 }}}, \
{.msg = {{0x394, 0, 8, .ignore_checksum = (legacy), .ignore_counter = (legacy), .max_counter = (legacy) ? 0U : 7U, .frequency = 100U}, { 0 }, { 0 }}}, \
static uint8_t hyundai_get_counter(const CANPacket_t *msg) {
#define HYUNDAI_SCC12_ADDR_CHECK(scc_bus) \
{.msg = {{0x421, (scc_bus), 8, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \
static bool hyundai_legacy = false;
static bool hyundai_cruise_buttons_alt = false;
static uint8_t hyundai_get_counter(const CANPacket_t *to_push) {
int addr = GET_ADDR(to_push);
uint8_t cnt = 0;
if (msg->addr == 0x260U) {
cnt = (msg->data[7] >> 4) & 0x3U;
} else if (msg->addr == 0x386U) {
cnt = ((msg->data[3] >> 6) << 2) | (msg->data[1] >> 6);
} else if (msg->addr == 0x394U) {
cnt = (msg->data[1] >> 5) & 0x7U;
} else if (msg->addr == 0x421U) {
cnt = msg->data[7] & 0xFU;
} else if (msg->addr == 0x4F1U) {
cnt = (msg->data[3] >> 4) & 0xFU;
if (addr == 0x260) {
cnt = (GET_BYTE(to_push, 7) >> 4) & 0x3U;
} else if (addr == 0x386) {
cnt = ((GET_BYTE(to_push, 3) >> 6) << 2) | (GET_BYTE(to_push, 1) >> 6);
} else if (addr == 0x394) {
cnt = (GET_BYTE(to_push, 1) >> 5) & 0x7U;
} else if (addr == 0x421) {
cnt = GET_BYTE(to_push, 7) & 0xFU;
} else if (addr == 0x4F1) {
cnt = (GET_BYTE(to_push, 3) >> 4) & 0xFU;
} else {
}
return cnt;
}
static uint32_t hyundai_get_checksum(const CANPacket_t *msg) {
static uint32_t hyundai_get_checksum(const CANPacket_t *to_push) {
int addr = GET_ADDR(to_push);
uint8_t chksum = 0;
if (msg->addr == 0x260U) {
chksum = msg->data[7] & 0xFU;
} else if (msg->addr == 0x386U) {
chksum = ((msg->data[7] >> 6) << 2) | (msg->data[5] >> 6);
} else if (msg->addr == 0x394U) {
chksum = msg->data[6] & 0xFU;
} else if (msg->addr == 0x421U) {
chksum = msg->data[7] >> 4;
if (addr == 0x260) {
chksum = GET_BYTE(to_push, 7) & 0xFU;
} else if (addr == 0x386) {
chksum = ((GET_BYTE(to_push, 7) >> 6) << 2) | (GET_BYTE(to_push, 5) >> 6);
} else if (addr == 0x394) {
chksum = GET_BYTE(to_push, 6) & 0xFU;
} else if (addr == 0x421) {
chksum = GET_BYTE(to_push, 7) >> 4;
} else {
}
return chksum;
}
static uint32_t hyundai_compute_checksum(const CANPacket_t *msg) {
static uint32_t hyundai_compute_checksum(const CANPacket_t *to_push) {
int addr = GET_ADDR(to_push);
uint8_t chksum = 0;
if (msg->addr == 0x386U) {
if (addr == 0x386) {
// count the bits
for (int i = 0; i < 8; i++) {
uint8_t b = msg->data[i];
uint8_t b = GET_BYTE(to_push, i);
for (int j = 0; j < 8; j++) {
uint8_t bit = 0;
// exclude checksum and counter
@@ -124,12 +114,12 @@ static uint32_t hyundai_compute_checksum(const CANPacket_t *msg) {
} else {
// sum of nibbles
for (int i = 0; i < 8; i++) {
if ((msg->addr == 0x394U) && (i == 7)) {
if ((addr == 0x394) && (i == 7)) {
continue; // exclude
}
uint8_t b = msg->data[i];
if (((msg->addr == 0x260U) && (i == 7)) || ((msg->addr == 0x394U) && (i == 6)) || ((msg->addr == 0x421U) && (i == 7))) {
b &= (msg->addr == 0x421U) ? 0x0FU : 0xF0U; // remove checksum
uint8_t b = GET_BYTE(to_push, i);
if (((addr == 0x260) && (i == 7)) || ((addr == 0x394) && (i == 6)) || ((addr == 0x421) && (i == 7))) {
b &= (addr == 0x421) ? 0x0FU : 0xF0U; // remove checksum
}
chksum += (b % 16U) + (b / 16U);
}
@@ -139,127 +129,115 @@ static uint32_t hyundai_compute_checksum(const CANPacket_t *msg) {
return chksum;
}
static void hyundai_rx_hook(const CANPacket_t *msg) {
static void hyundai_rx_hook(const CANPacket_t *to_push) {
int bus = GET_BUS(to_push);
int addr = GET_ADDR(to_push);
// SCC12 is on bus 2 for camera-based SCC cars, bus 0 on all others
if (msg->addr == 0x421U) {
if (((msg->bus == 0U) && !hyundai_camera_scc) || ((msg->bus == 2U) && hyundai_camera_scc)) {
if (addr == 0x421) {
if (((bus == 0) && !hyundai_camera_scc) || ((bus == 2) && hyundai_camera_scc)) {
// 2 bits: 13-14
int cruise_engaged = (GET_BYTES(msg, 0, 4) >> 13) & 0x3U;
int cruise_engaged = (GET_BYTES(to_push, 0, 4) >> 13) & 0x3U;
hyundai_common_cruise_state_check(cruise_engaged);
}
}
if (msg->addr == 0x420U) {
if (((msg->bus == 0U) && !hyundai_camera_scc) || ((msg->bus == 2U) && hyundai_camera_scc)) {
if (!hyundai_longitudinal) {
acc_main_on = GET_BIT(msg, 0U);
}
}
}
if (msg->bus == 0U) {
if (msg->addr == 0x251U) {
int torque_driver_new = (GET_BYTES(msg, 0, 2) & 0x7ffU) - 1024U;
if (bus == 0) {
if (addr == 0x251) {
int torque_driver_new = (GET_BYTES(to_push, 0, 2) & 0x7ffU) - 1024U;
// update array of samples
update_sample(&torque_driver, torque_driver_new);
}
if (msg->addr == 0x391U) {
aol_button_press = GET_BIT(msg, 4U) ? AOL_BUTTON_PRESSED : AOL_BUTTON_NOT_PRESSED;
}
// ACC steering wheel buttons
if (msg->addr == 0x4F1U) {
int cruise_button = msg->data[0] & 0x7U;
bool main_button = GET_BIT(msg, 3U);
if (addr == 1007) hyundai_cruise_buttons_alt = true; // CASPER_EV: 1007
if (addr == 1007) {
int cruise_button = (GET_BYTE(to_push, 7) >> 4) & 0x07U;
bool main_button = GET_BIT(to_push, 58U);
hyundai_common_cruise_buttons_check(cruise_button, main_button);
}
else if (addr == 0x4F1 && !hyundai_cruise_buttons_alt) {
int cruise_button = GET_BYTE(to_push, 0) & 0x7U;
bool main_button = GET_BIT(to_push, 3U);
hyundai_common_cruise_buttons_check(cruise_button, main_button);
}
// gas press, different for EV, hybrid, and ICE models
if ((msg->addr == 0x371U) && hyundai_ev_gas_signal) {
gas_pressed = (((msg->data[4] & 0x7FU) << 1) | (msg->data[3] >> 7)) != 0U;
} else if ((msg->addr == 0x371U) && hyundai_hybrid_gas_signal) {
gas_pressed = msg->data[7] != 0U;
} else if ((msg->addr == 0x91U) && hyundai_fcev_gas_signal) {
gas_pressed = msg->data[6] != 0U;
} else if ((msg->addr == 0x260U) && !hyundai_ev_gas_signal && !hyundai_hybrid_gas_signal) {
gas_pressed = (msg->data[7] >> 6) != 0U;
if ((addr == 0x371) && hyundai_ev_gas_signal) {
gas_pressed = (((GET_BYTE(to_push, 4) & 0x7FU) << 1) | GET_BYTE(to_push, 3) >> 7) != 0U;
} else if ((addr == 0x371) && hyundai_hybrid_gas_signal) {
gas_pressed = GET_BYTE(to_push, 7) != 0U;
} else if ((addr == 0x91) && hyundai_fcev_gas_signal) {
gas_pressed = GET_BYTE(to_push, 6) != 0U;
} else if ((addr == 0x260) && !hyundai_ev_gas_signal && !hyundai_hybrid_gas_signal) {
gas_pressed = (GET_BYTE(to_push, 7) >> 6) != 0U;
} else {
}
// sample wheel speed, averaging opposite corners
if (msg->addr == 0x386U) {
uint32_t front_left_speed = GET_BYTES(msg, 0, 2) & 0x3FFFU;
uint32_t rear_right_speed = GET_BYTES(msg, 6, 2) & 0x3FFFU;
if (addr == 0x386) {
uint32_t front_left_speed = GET_BYTES(to_push, 0, 2) & 0x3FFFU;
uint32_t rear_right_speed = GET_BYTES(to_push, 6, 2) & 0x3FFFU;
vehicle_moving = (front_left_speed > HYUNDAI_STANDSTILL_THRSLD) || (rear_right_speed > HYUNDAI_STANDSTILL_THRSLD);
}
if (msg->addr == 0x394U) {
brake_pressed = ((msg->data[5] >> 5U) & 0x3U) == 0x2U;
if (addr == 0x394) {
brake_pressed = ((GET_BYTE(to_push, 5) >> 5U) & 0x3U) == 0x2U;
}
if (msg->addr == 0x592U) {
acc_main_on = GET_BIT(msg, 34U);
bool cruise_engaged = GET_BIT(msg, 35U);
hyundai_common_cruise_state_check(cruise_engaged);
}
bool stock_ecu_detected = (addr == 0x340);
if (msg->addr == 0x595U) {
acc_main_on = GET_BIT(msg, 50U);
bool cruise_engaged = GET_BIT(msg, 51U);
hyundai_common_cruise_state_check(cruise_engaged);
}
if ((msg->addr == 0x260U) && hyundai_non_scc && !hyundai_ev_gas_signal && !hyundai_hybrid_gas_signal) {
acc_main_on = GET_BIT(msg, 25U);
bool cruise_engaged = GET_BIT(msg, 26U);
hyundai_common_cruise_state_check(cruise_engaged);
// 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_check(stock_ecu_detected);
}
hyundai_common_reset_acc_main_on_mismatches();
}
static bool hyundai_tx_hook(const CANPacket_t *msg) {
const TorqueSteeringLimits HYUNDAI_STEERING_LIMITS = HYUNDAI_LIMITS(384, 3, 7);
const TorqueSteeringLimits HYUNDAI_STEERING_LIMITS_ALT = HYUNDAI_LIMITS(270, 2, 3);
uint32_t last_ts_lkas11_from_op = 0;
uint32_t last_ts_scc12_from_op = 0;
uint32_t last_ts_scc13_from_op = 0;
uint32_t last_ts_mdps12_from_op = 0;
uint32_t last_ts_fca11_from_op = 0;
uint32_t last_ts_fca12_from_op = 0;
uint32_t last_ts_lfahda_mfc_from_op = 0;
static bool hyundai_tx_hook(const CANPacket_t *to_send) {
const TorqueSteeringLimits HYUNDAI_STEERING_LIMITS = HYUNDAI_LIMITS(512, 10, 10);
const TorqueSteeringLimits HYUNDAI_STEERING_LIMITS_ALT = HYUNDAI_LIMITS(512, 10, 10);
const TorqueSteeringLimits HYUNDAI_STEERING_LIMITS_ALT_2 = HYUNDAI_LIMITS(170, 2, 3);
bool tx = true;
int addr = GET_ADDR(to_send);
// FCA11: Block any potential actuation
if (msg->addr == 0x38DU) {
int CR_VSM_DecCmd = msg->data[1];
bool FCA_CmdAct = GET_BIT(msg, 20U);
bool CF_VSM_DecCmdAct = GET_BIT(msg, 31U);
if (addr == 0x38D) {
int CR_VSM_DecCmd = GET_BYTE(to_send, 1);
bool FCA_CmdAct = GET_BIT(to_send, 20U);
bool CF_VSM_DecCmdAct = GET_BIT(to_send, 31U);
if ((CR_VSM_DecCmd != 0) || FCA_CmdAct || CF_VSM_DecCmdAct) {
tx = false;
}
}
if (msg->addr == 0x420U) {
acc_main_on_tx = GET_BIT(msg, 0U);
hyundai_common_acc_main_on_sync();
}
// ACCEL: safety check
if (msg->addr == 0x421U) {
int desired_accel_raw = (((msg->data[4] & 0x7U) << 8) | msg->data[3]) - 1023U;
int desired_accel_val = ((msg->data[5] << 3) | (msg->data[4] >> 5)) - 1023U;
if (addr == 0x421) {
int desired_accel_raw = (((GET_BYTE(to_send, 4) & 0x7U) << 8) | GET_BYTE(to_send, 3)) - 1023U;
int desired_accel_val = ((GET_BYTE(to_send, 5) << 3) | (GET_BYTE(to_send, 4) >> 5)) - 1023U;
int aeb_decel_cmd = msg->data[2];
bool aeb_req = GET_BIT(msg, 54U);
int aeb_decel_cmd = GET_BYTE(to_send, 2);
bool aeb_req = GET_BIT(to_send, 54U);
bool violation = false;
violation |= longitudinal_accel_checks(desired_accel_raw, HYUNDAI_LONG_LIMITS);
violation |= longitudinal_accel_checks(desired_accel_val, HYUNDAI_LONG_LIMITS);
if (!hyundai_escc) {
violation |= (aeb_decel_cmd != 0);
violation |= aeb_req;
}
violation |= (aeb_decel_cmd != 0);
violation |= aeb_req;
if (violation) {
tx = false;
@@ -267,9 +245,9 @@ static bool hyundai_tx_hook(const CANPacket_t *msg) {
}
// LKA STEER: safety check
if (msg->addr == 0x340U) {
int desired_torque = ((GET_BYTES(msg, 0, 4) >> 16) & 0x7ffU) - 1024U;
bool steer_req = GET_BIT(msg, 27U);
if (addr == 0x340) {
int desired_torque = ((GET_BYTES(to_send, 0, 4) >> 16) & 0x7ffU) - 1024U;
bool steer_req = GET_BIT(to_send, 27U);
const TorqueSteeringLimits limits = hyundai_alt_limits_2 ? HYUNDAI_STEERING_LIMITS_ALT_2 :
hyundai_alt_limits ? HYUNDAI_STEERING_LIMITS_ALT : HYUNDAI_STEERING_LIMITS;
@@ -280,220 +258,199 @@ static bool hyundai_tx_hook(const CANPacket_t *msg) {
}
// UDS: Only tester present ("\x02\x3E\x80\x00\x00\x00\x00\x00") allowed on diagnostics address
if (msg->addr == 0x7D0U) {
if ((GET_BYTES(msg, 0, 4) != 0x00803E02U) || (GET_BYTES(msg, 4, 4) != 0x0U)) {
if (addr == 0x7D0) {
if ((GET_BYTES(to_send, 0, 4) != 0x00803E02U) || (GET_BYTES(to_send, 4, 4) != 0x0U)) {
tx = false;
}
}
// BUTTONS: used for resume spamming and cruise cancellation
if ((msg->addr == 0x4F1U) && !hyundai_longitudinal) {
int button = msg->data[0] & 0x7U;
if ((addr == 0x4F1) && !hyundai_longitudinal) {
int button = GET_BYTE(to_send, 0) & 0x7U;
bool allowed_resume = (button == 1) && controls_allowed;
bool allowed_set = (button == 2) && controls_allowed;
bool allowed_cancel = (button == 4) && cruise_engaged_prev;
if (!(allowed_resume || allowed_set || allowed_cancel)) {
bool allowed_cancel = (button == 4) && (cruise_engaged_prev || controls_allowed);
if (!(allowed_resume || allowed_cancel)) {
tx = false;
}
}
uint32_t now = microsecond_timer_get();
if(addr == 832)
last_ts_lkas11_from_op = (tx == 0 ? 0 : now);
else if(addr == 1057)
last_ts_scc12_from_op = (tx == 0 ? 0 : now);
else if(addr == 593)
last_ts_mdps12_from_op = (tx == 0 ? 0 : now);
else if (addr == 909)
last_ts_fca11_from_op = (tx == 0 ? 0 : now);
else if (addr == 1155)
last_ts_fca12_from_op = (tx == 0 ? 0 : now);
else if(addr == 1290)
last_ts_scc13_from_op = (tx == 0 ? 0 : now);
else if(addr == 0x485)
last_ts_lfahda_mfc_from_op = (tx == 0 ? 0 : now);
return tx;
}
static safety_config hyundai_init(uint16_t param) {
static const CanMsg HYUNDAI_LONG_TX_MSGS[] = {
HYUNDAI_LONG_COMMON_TX_MSGS(0)
{0x38D, 0, 8, .check_relay = false}, // FCA11 Bus 0
{0x483, 0, 8, .check_relay = false}, // FCA12 Bus 0
{0x7D0, 0, 8, .check_relay = false}, // radar UDS TX addr Bus 0 (for radar disable)
};
static bool hyundai_fwd_hook(int bus_num, int addr) {
int bus_fwd = -1;
static const CanMsg HYUNDAI_CAMERA_SCC_TX_MSGS[] = {
HYUNDAI_COMMON_TX_MSGS(2)
};
uint32_t now = microsecond_timer_get();
static const CanMsg HYUNDAI_CAMERA_SCC_LONG_TX_MSGS[] = {
HYUNDAI_LONG_COMMON_TX_MSGS(2)
};
// forward cam to ccan and viceversa, except lkas cmd
if (bus_num == 0) {
bus_fwd = 2;
static const CanMsg HYUNDAI_LONG_ESCC_TX_MSGS[] = {
HYUNDAI_LONG_COMMON_TX_MSGS(0)
};
hyundai_common_init(param);
hyundai_legacy = false;
safety_config ret;
if (hyundai_longitudinal) {
// Use CLU11 (buttons) to manage controls allowed instead of SCC cruise state
static RxCheck hyundai_long_rx_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
};
static RxCheck hyundai_lda_button_long_rx_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_LDA_BUTTON_ADDR_CHECK
};
static RxCheck hyundai_fcev_long_rx_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_FCEV_GAS_ADDR_CHECK
};
static RxCheck hyundai_fcev_lda_button_long_rx_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_FCEV_GAS_ADDR_CHECK
HYUNDAI_LDA_BUTTON_ADDR_CHECK
};
if (hyundai_fcev_gas_signal) {
if (hyundai_has_lda_button) {
SET_RX_CHECKS(hyundai_fcev_lda_button_long_rx_checks, ret);
} else {
SET_RX_CHECKS(hyundai_fcev_long_rx_checks, ret);
}
} else {
if (hyundai_has_lda_button) {
SET_RX_CHECKS(hyundai_lda_button_long_rx_checks, ret);
} else {
SET_RX_CHECKS(hyundai_long_rx_checks, ret);
}
}
if (hyundai_escc) {
SET_TX_MSGS(HYUNDAI_LONG_ESCC_TX_MSGS, ret);
} else if (hyundai_camera_scc) {
SET_TX_MSGS(HYUNDAI_CAMERA_SCC_LONG_TX_MSGS, ret);
} else {
SET_TX_MSGS(HYUNDAI_LONG_TX_MSGS, ret);
}
} else if (hyundai_camera_scc) {
static RxCheck hyundai_cam_scc_rx_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_SCC12_ADDR_CHECK(2)
HYUNDAI_SCC11_ADDR_CHECK(2)
HYUNDAI_LDA_BUTTON_ADDR_CHECK
};
ret = BUILD_SAFETY_CFG(hyundai_cam_scc_rx_checks, HYUNDAI_CAMERA_SCC_TX_MSGS);
} else {
static RxCheck hyundai_rx_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_SCC12_ADDR_CHECK(0)
HYUNDAI_SCC11_ADDR_CHECK(0)
};
static RxCheck hyundai_lda_button_rx_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_SCC12_ADDR_CHECK(0)
HYUNDAI_SCC11_ADDR_CHECK(0)
HYUNDAI_LDA_BUTTON_ADDR_CHECK
};
static RxCheck hyundai_fcev_rx_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_SCC12_ADDR_CHECK(0)
HYUNDAI_SCC11_ADDR_CHECK(0)
HYUNDAI_FCEV_GAS_ADDR_CHECK
};
static RxCheck hyundai_fcev_lda_button_rx_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_SCC12_ADDR_CHECK(0)
HYUNDAI_SCC11_ADDR_CHECK(0)
HYUNDAI_FCEV_GAS_ADDR_CHECK
HYUNDAI_LDA_BUTTON_ADDR_CHECK
};
static RxCheck hyundai_non_scc_addr_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
};
static RxCheck hyundai_non_scc_lda_button_addr_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_LDA_BUTTON_ADDR_CHECK
};
static RxCheck hyundai_hev_non_scc_addr_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_NON_SCC_HEV_ADDR_CHECK
};
static RxCheck hyundai_hev_non_scc_lda_button_addr_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_NON_SCC_HEV_ADDR_CHECK
HYUNDAI_LDA_BUTTON_ADDR_CHECK
};
static RxCheck hyundai_ev_non_scc_addr_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_NON_SCC_EV_ADDR_CHECK
};
static RxCheck hyundai_ev_non_scc_lda_button_addr_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_NON_SCC_EV_ADDR_CHECK
HYUNDAI_LDA_BUTTON_ADDR_CHECK
};
SET_TX_MSGS(HYUNDAI_TX_MSGS, ret);
if (hyundai_fcev_gas_signal) {
if (hyundai_has_lda_button) {
SET_RX_CHECKS(hyundai_fcev_lda_button_rx_checks, ret);
} else {
SET_RX_CHECKS(hyundai_fcev_rx_checks, ret);
}
} else if (hyundai_non_scc) {
if (hyundai_ev_gas_signal) {
if (hyundai_has_lda_button) {
SET_RX_CHECKS(hyundai_ev_non_scc_lda_button_addr_checks, ret);
} else {
SET_RX_CHECKS(hyundai_ev_non_scc_addr_checks, ret);
}
} else if (hyundai_hybrid_gas_signal) {
if (hyundai_has_lda_button) {
SET_RX_CHECKS(hyundai_hev_non_scc_lda_button_addr_checks, ret);
} else {
SET_RX_CHECKS(hyundai_hev_non_scc_addr_checks, ret);
}
} else {
if (hyundai_has_lda_button) {
SET_RX_CHECKS(hyundai_non_scc_lda_button_addr_checks, ret);
} else {
SET_RX_CHECKS(hyundai_non_scc_addr_checks, ret);
}
}
} else {
if (hyundai_has_lda_button) {
SET_RX_CHECKS(hyundai_lda_button_rx_checks, ret);
} else {
SET_RX_CHECKS(hyundai_rx_checks, ret);
if(addr == 593) {
if(now - last_ts_mdps12_from_op < 200000) {
bus_fwd = -1;
}
}
}
return ret;
if (bus_num == 2) {
bool is_lkas_msg = addr == 832;
bool is_lfahda_msg = addr == 1157;
bool is_scc_msg = addr == 1056 || addr == 1057 || addr == 905;
bool is_scc13_msg = addr == 1290;
bool is_fca11_msg = addr == 909;
bool is_fca12_msg = addr == 1155;
bool block_msg = is_lkas_msg || is_lfahda_msg || is_scc_msg || is_scc13_msg || is_fca11_msg || is_fca12_msg;
if (!block_msg) {
bus_fwd = 0;
}
else {
if(is_lkas_msg) {
if(now - last_ts_lkas11_from_op >= 200000) {
bus_fwd = 0;
}
}
else if(is_lfahda_msg) {
if (now - last_ts_lfahda_mfc_from_op >= 200000)
bus_fwd = 0;
}
else if (is_scc_msg) {
if (now - last_ts_scc12_from_op >= 400000)
bus_fwd = 0;
}
else if (is_scc13_msg) {
if (now - last_ts_scc13_from_op >= 800000)
bus_fwd = 0;
}
else if (is_fca11_msg) {
if (now - last_ts_fca11_from_op >= 400000)
bus_fwd = 0;
}
else if (is_fca12_msg) {
if (now - last_ts_fca12_from_op >= 400000)
bus_fwd = 0;
}
}
}
return bus_fwd == -1;
}
/* case
- legacy(on/off) + camera_scc(allways longitudinal on) + longitudinal(scc off)
*/
static safety_config hyundai_init_carrot(bool legacy_car) {
static const CanMsg HYUNDAI_LONG_TX_MSGS[] = {
{0x340, 0, 8}, // LKAS11 Bus 0
{0x4F1, 0, 4}, // CLU11 Bus 0
{0x485, 0, 8}, // LFAHDA_MFC Bus 0
{0x251, 2, 8}, // MDPS12 Bus 2
{0x420, 0, 8}, // SCC11 Bus 0
{0x421, 0, 8}, // SCC12 Bus 0
{0x50A, 0, 8}, // SCC13 Bus 0
{0x389, 0, 8}, // SCC14 Bus 0
{0x4A2, 0, 2}, // FRT_RADAR11 Bus 0
{0x38D, 0, 8}, // FCA11 Bus 0
{0x483, 0, 8}, // FCA12 Bus 0
{0x7D0, 0, 8}, // radar UDS TX addr Bus 0 (for radar disable)
};
static const CanMsg HYUNDAI_CAMERA_SCC_TX_MSGS[] = {
{0x340, 0, 8}, // LKAS11 Bus 0
{0x4F1, 2, 4}, // CLU11 Bus 2
{0x485, 0, 8}, // LFAHDA_MFC Bus 0
{593, 2, 8}, // MDPS12, Bus 2
{1056, 0, 8}, // SCC11, Bus 0
{1057, 0, 8}, // SCC12, Bus 0
{1290, 0, 8}, // SCC13, Bus 0
{905, 0, 8}, // SCC14, Bus 0
{909, 0, 8}, // FCA11 Bus 0
{1155, 0, 8}, // FCA12 Bus 0
{1186, 0, 8}, // FRT_RADAR11, Bus 0
{0x4F1, 0, 4}, // CLU11 Bus 0
};
safety_config ret;
if (hyundai_camera_scc) {
static RxCheck hyundai_cam_scc_rx_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_SCC12_ADDR_CHECK(2)
};
static RxCheck hyundai_cam_scc_rx_checks_legacy[] = {
HYUNDAI_COMMON_RX_CHECKS(true)
HYUNDAI_SCC12_ADDR_CHECK(2)
};
if(legacy_car) ret = BUILD_SAFETY_CFG(hyundai_cam_scc_rx_checks_legacy, HYUNDAI_CAMERA_SCC_TX_MSGS);
else ret = BUILD_SAFETY_CFG(hyundai_cam_scc_rx_checks, HYUNDAI_CAMERA_SCC_TX_MSGS);
}
else if (hyundai_longitudinal) {
static RxCheck hyundai_long_rx_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
// Use CLU11 (buttons) to manage controls allowed instead of SCC cruise state
{.msg = {{0x4F1, 0, 4, .ignore_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}
},
};
static RxCheck hyundai_long_rx_checks_legacy[] = {
HYUNDAI_COMMON_RX_CHECKS(true)
// Use CLU11 (buttons) to manage controls allowed instead of SCC cruise state
{.msg = {{0x4F1, 0, 4, .ignore_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}
},
};
if(legacy_car) ret = BUILD_SAFETY_CFG(hyundai_long_rx_checks_legacy, HYUNDAI_LONG_TX_MSGS);
else ret = BUILD_SAFETY_CFG(hyundai_long_rx_checks, HYUNDAI_LONG_TX_MSGS);
}
else {
static RxCheck hyundai_rx_checks[] = {
HYUNDAI_COMMON_RX_CHECKS(false)
HYUNDAI_SCC12_ADDR_CHECK(0)
};
static RxCheck hyundai_rx_checks_legacy[] = {
HYUNDAI_COMMON_RX_CHECKS(true)
//HYUNDAI_SCC12_ADDR_CHECK(0)
};
if(legacy_car) ret = BUILD_SAFETY_CFG(hyundai_rx_checks_legacy, HYUNDAI_TX_MSGS);
else ret = BUILD_SAFETY_CFG(hyundai_rx_checks, HYUNDAI_TX_MSGS);
}
return ret;
}
static safety_config hyundai_init(uint16_t param) {
hyundai_common_init(param);
hyundai_legacy = false;
return hyundai_init_carrot(hyundai_legacy);
}
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_SCC11_ADDR_CHECK(0)
};
hyundai_common_init(param);
hyundai_legacy = true;
hyundai_longitudinal = false;
hyundai_camera_scc = false;
return BUILD_SAFETY_CFG(hyundai_legacy_rx_checks, HYUNDAI_TX_MSGS);
return hyundai_init_carrot(hyundai_legacy);
}
const safety_hooks hyundai_hooks = {
.init = hyundai_init,
.rx = hyundai_rx_hook,
.tx = hyundai_tx_hook,
.fwd = hyundai_fwd_hook,
.get_counter = hyundai_get_counter,
.get_checksum = hyundai_get_checksum,
.compute_checksum = hyundai_compute_checksum,
@@ -503,6 +460,7 @@ const safety_hooks hyundai_legacy_hooks = {
.init = hyundai_legacy_init,
.rx = hyundai_rx_hook,
.tx = hyundai_tx_hook,
.fwd = hyundai_fwd_hook,
.get_counter = hyundai_get_counter,
.get_checksum = hyundai_get_checksum,
.compute_checksum = hyundai_compute_checksum,

View File

@@ -3,149 +3,550 @@
#include "iqdbc/safety/declarations.h"
#include "iqdbc/safety/modes/hyundai_common.h"
#define HYUNDAI_CANFD_CRUISE_BUTTON_TX_MSGS(bus) \
{0x1CF, bus, 8, .check_relay = false}, /* CRUISE_BUTTON */ \
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#pragma GCC diagnostic ignored "-Wunused-function"
#define HYUNDAI_CANFD_LKA_STEERING_COMMON_TX_MSGS(a_can, e_can) \
HYUNDAI_CANFD_CRUISE_BUTTON_TX_MSGS(e_can) \
{0x50, a_can, 16, .check_relay = (a_can) == 0}, /* LKAS */ \
{0x2A4, a_can, 24, .check_relay = (a_can) == 0}, /* CAM_0x2A4 */ \
const TorqueSteeringLimits HYUNDAI_CANFD_STEERING_LIMITS = {
.max_torque = 512, //270,
.max_rt_delta = 112,
#define HYUNDAI_CANFD_LKA_STEERING_ALT_COMMON_TX_MSGS(a_can, e_can) \
HYUNDAI_CANFD_CRUISE_BUTTON_TX_MSGS(e_can) \
{0x110, a_can, 32, .check_relay = (a_can) == 0}, /* LKAS_ALT */ \
{0x362, a_can, 32, .check_relay = (a_can) == 0}, /* CAM_0x362 */ \
.max_rate_up = 2,
.max_rate_down = 3,
.driver_torque_allowance = 250,
.driver_torque_multiplier = 2,
.type = TorqueDriverLimited,
#define HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(e_can) \
{0x12A, e_can, 16, .check_relay = (e_can) == 0}, /* LFA */ \
{0x1E0, e_can, 16, .check_relay = (e_can) == 0}, /* LFAHDA_CLUSTER */ \
// the EPS faults when the steering angle is above a certain threshold for too long. to prevent this,
// we allow setting torque actuation bit to 0 while maintaining the requested torque value for two consecutive frames
.min_valid_request_frames = 89,
.max_invalid_request_frames = 2,
.min_valid_request_rt_interval = 810000, // 810ms; a ~10% buffer on cutting every 90 frames
.has_steer_req_tolerance = true,
};
const CanMsg HYUNDAI_CANFD_HDA2_TX_MSGS[] = {
{0x50, 0, 16}, // LKAS
{0x1CF, 1, 8}, // CRUISE_BUTTON
{0x2A4, 0, 24}, // CAM_0x2A4
};
const CanMsg HYUNDAI_CANFD_HDA2_ALT_STEERING_TX_MSGS[] = {
{0x110, 0, 32}, // LKAS_ALT
{0x1CF, 1, 8}, // CRUISE_BUTTON
{0x362, 0, 32}, // CAM_0x362
{0x1AA, 1, 16}, // CRUISE_ALT_BUTTONS , carrot
};
const CanMsg HYUNDAI_CANFD_HDA2_LONG_TX_MSGS[] = {
{0x50, 0, 16}, // LKAS
{0x1CF, 0, 8}, // CRUISE_BUTTON
{0x1CF, 1, 8}, // CRUISE_BUTTON
{0x1CF, 2, 8}, // CRUISE_BUTTON
{0x1AA, 0, 16}, // CRUISE_ALT_BUTTONS , carrot
{0x1AA, 1, 16}, // CRUISE_ALT_BUTTONS , carrot
{0x1AA, 2, 16}, // CRUISE_ALT_BUTTONS , carrot
{0x2A4, 0, 24}, // CAM_0x2A4
{0x51, 0, 32}, // ADRV_0x51
{0x730, 1, 8}, // tester present for ADAS ECU disable
{0x12A, 1, 16}, // LFA
{0x160, 1, 16}, // ADRV_0x160
{0x1E0, 1, 16}, // LFAHDA_CLUSTER
{0x1A0, 1, 32}, // CRUISE_INFO
{0x1EA, 1, 32}, // ADRV_0x1ea
{0x200, 1, 8}, // ADRV_0x200
{0x345, 1, 8}, // ADRV_0x345
{0x1DA, 1, 32}, // ADRV_0x1da
{0x12A, 0, 16}, // LFA
{0x1E0, 0, 16}, // LFAHDA_CLUSTER
{0x160, 0, 16}, // ADRV_0x160
{0x1EA, 0, 32}, // ADRV_0x1ea
{0x200, 0, 8}, // ADRV_0x200
{0x1A0, 0, 32}, // CRUISE_INFO
{0x345, 0, 8}, // ADRV_0x345
{0x1DA, 0, 32}, // ADRV_0x1da
{0x362, 0, 32}, // CAM_0x362
{0x362, 1, 32}, // CAM_0x362
{0x2a4, 1, 24}, // CAM_0x2a4
{0x110, 0, 32}, // LKAS_ALT (272)
{0x110, 1, 32}, // LKAS_ALT (272)
{0x50, 1, 16}, //
{0x51, 1, 32}, //
{353, 0, 32}, // ADRV_353
{354, 0, 32}, // CORNER_RADAR_HIGHWAY
{512, 0, 8}, // ADRV_0x200
{1187, 2, 8}, // 4A3
{1204, 2, 8}, // 4B4
{203, 0, 24}, // CB
{373, 2, 24}, // TCS(0x175)
{506, 2, 32}, // CLUSTER_SPEED_LIMIT
{234, 2, 24}, // MDPS
{687, 2, 8}, // STEER_TOUCH_2AF
{0x4BE, 2, 8}, // NEW_MSG_4BE (may be corner radar enabler x)
{0x4B9, 2, 8}, // NEW_MSG_4B9 (may be corner radar enabler)
};
const CanMsg HYUNDAI_CANFD_HDA1_TX_MSGS[] = {
{0x12A, 0, 16}, // LFA
{0x1A0, 0, 32}, // CRUISE_INFO
{0x1CF, 0, 8}, // CRUISE_BUTTON
{0x1CF, 2, 8}, // CRUISE_BUTTON
{0x1E0, 0, 16}, // LFAHDA_CLUSTER
{0x160, 0, 16}, // ADRV_0x160
{0x7D0, 0, 8}, // tester present for radar ECU disable
{0x1AA, 2, 16}, // CRUISE_ALT_BUTTONS , carrot
{203, 0, 24}, // CB
{373, 2, 24}, // TCS(0x175)
{353, 0, 32}, // ADRV_353
{354, 0, 32}, // CORNER_RADAR_HIGHWAY
{512, 0, 8}, // ADRV_0x200
{1187, 2, 8}, // 4A3
{1204, 2, 8}, // 4B4
{373, 2, 24}, // TCS(0x175)
{234, 2, 24}, // MDPS
{687, 2, 8}, // STEER_TOUCH_2AF
};
#define HYUNDAI_CANFD_SCC_CONTROL_COMMON_TX_MSGS(e_can, longitudinal) \
{0x1A0, e_can, 32, .check_relay = (longitudinal)}, /* SCC_CONTROL */ \
// *** Addresses checked in rx hook ***
// EV, ICE, HYBRID: ACCELERATOR (0x35), ACCELERATOR_BRAKE_ALT (0x100), ACCELERATOR_ALT (0x105)
#define HYUNDAI_CANFD_COMMON_RX_CHECKS(pt_bus) \
{.msg = {{0x35, (pt_bus), 32, 100U, .max_counter = 0xffU, .ignore_quality_flag = true}, \
{0x100, (pt_bus), 32, 100U, .max_counter = 0xffU, .ignore_quality_flag = true}, \
{0x105, (pt_bus), 32, 100U, .max_counter = 0xffU, .ignore_quality_flag = true}}}, \
{.msg = {{0x175, (pt_bus), 24, 50U, .max_counter = 0xffU, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{0xa0, (pt_bus), 24, 100U, .max_counter = 0xffU, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{0xea, (pt_bus), 24, 100U, .max_counter = 0xffU, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
#define HYUNDAI_CANFD_COMMON_RX_CHECKS(pt_bus) \
{.msg = {{0x35, (pt_bus), 32, .max_counter = 0xffU, .frequency = 100U}, \
{0x100, (pt_bus), 32, .max_counter = 0xffU, .frequency = 100U}, \
{0x105, (pt_bus), 32, .max_counter = 0xffU, .frequency = 100U}}}, \
{.msg = {{0x175, (pt_bus), 24, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, \
{.msg = {{0xa0, (pt_bus), 24, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, \
{.msg = {{0xea, (pt_bus), 24, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, \
#define HYUNDAI_CANFD_STD_BUTTONS_RX_CHECKS(pt_bus) \
HYUNDAI_CANFD_COMMON_RX_CHECKS(pt_bus) \
{.msg = {{0x1cf, (pt_bus), 8, 50U, .ignore_checksum = true, .max_counter = 0xfU, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
#define HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(pt_bus) \
{.msg = {{0x1cf, (pt_bus), 8, .ignore_checksum = true, .max_counter = 0xfU, .frequency = 50U}, { 0 }, { 0 }}}, \
#define HYUNDAI_CANFD_ALT_BUTTONS_RX_CHECKS(pt_bus) \
HYUNDAI_CANFD_COMMON_RX_CHECKS(pt_bus) \
{.msg = {{0x1aa, (pt_bus), 16, 50U, .ignore_checksum = true, .max_counter = 0xffU, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
#define HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(pt_bus) \
{.msg = {{0x1aa, (pt_bus), 16, .ignore_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, \
// SCC_CONTROL (from ADAS unit or camera)
#define HYUNDAI_CANFD_SCC_ADDR_CHECK(scc_bus) \
{.msg = {{0x1a0, (scc_bus), 32, 50U, .max_counter = 0xffU, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
#define HYUNDAI_CANFD_SCC_ADDR_CHECK(scc_bus) \
{.msg = {{0x1a0, (scc_bus), 32, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, \
static bool hyundai_canfd_alt_buttons = false;
static bool hyundai_canfd_lka_steering_alt = false;
//static bool hyundai_canfd_alt_buttons = false;
//static bool hyundai_canfd_hda2_alt_steering = false;
static unsigned int hyundai_canfd_get_lka_addr(void) {
return hyundai_canfd_lka_steering_alt ? 0x110U : 0x50U;
// *** Non-HDA2 checks ***
// Camera sends SCC messages on HDA1.
// Both button messages exist on some platforms, so we ensure we track the correct one using flag
RxCheck hyundai_canfd_rx_checks[] = {
HYUNDAI_CANFD_COMMON_RX_CHECKS(0)
HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(0)
HYUNDAI_CANFD_SCC_ADDR_CHECK(2)
};
RxCheck hyundai_canfd_alt_buttons_rx_checks[] = {
HYUNDAI_CANFD_COMMON_RX_CHECKS(0)
HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(0)
HYUNDAI_CANFD_SCC_ADDR_CHECK(2)
};
// Longitudinal checks for HDA1
RxCheck hyundai_canfd_long_rx_checks[] = {
HYUNDAI_CANFD_COMMON_RX_CHECKS(0)
HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(0)
};
RxCheck hyundai_canfd_long_alt_buttons_rx_checks[] = {
HYUNDAI_CANFD_COMMON_RX_CHECKS(0)
HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(0)
};
// Radar sends SCC messages on these cars instead of camera
RxCheck hyundai_canfd_radar_scc_rx_checks[] = {
HYUNDAI_CANFD_COMMON_RX_CHECKS(0)
HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(0)
HYUNDAI_CANFD_SCC_ADDR_CHECK(0)
};
RxCheck hyundai_canfd_radar_scc_alt_buttons_rx_checks[] = {
HYUNDAI_CANFD_COMMON_RX_CHECKS(0)
HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(0)
HYUNDAI_CANFD_SCC_ADDR_CHECK(0)
};
// *** HDA2 checks ***
// E-CAN is on bus 1, ADAS unit sends SCC messages on HDA2.
// Does not use the alt buttons message
RxCheck hyundai_canfd_hda2_rx_checks[] = {
HYUNDAI_CANFD_COMMON_RX_CHECKS(1)
HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(1) // TODO: carrot: canival no 0x1cf
HYUNDAI_CANFD_SCC_ADDR_CHECK(1)
};
RxCheck hyundai_canfd_hda2_rx_checks_scc2[] = {
HYUNDAI_CANFD_COMMON_RX_CHECKS(0)
HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(0) // TODO: carrot: canival no 0x1cf
HYUNDAI_CANFD_SCC_ADDR_CHECK(2)
};
RxCheck hyundai_canfd_hda2_alt_buttons_rx_checks[] = {
HYUNDAI_CANFD_COMMON_RX_CHECKS(1)
HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(1)
HYUNDAI_CANFD_SCC_ADDR_CHECK(1)
};
RxCheck hyundai_canfd_hda2_alt_buttons_rx_checks_scc2[] = {
HYUNDAI_CANFD_COMMON_RX_CHECKS(0)
HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(0)
HYUNDAI_CANFD_SCC_ADDR_CHECK(2)
};
RxCheck hyundai_canfd_hda2_long_rx_checks[] = {
HYUNDAI_CANFD_COMMON_RX_CHECKS(1)
HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(1) // TODO: carrot: canival no 0x1cf
};
RxCheck hyundai_canfd_hda2_long_rx_checks_scc2[] = {
HYUNDAI_CANFD_COMMON_RX_CHECKS(0)
HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(0)
};
RxCheck hyundai_canfd_hda2_long_alt_buttons_rx_checks[] = {
HYUNDAI_CANFD_COMMON_RX_CHECKS(1)
HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(1)
};
RxCheck hyundai_canfd_hda2_long_alt_buttons_rx_checks_scc2[] = {
HYUNDAI_CANFD_COMMON_RX_CHECKS(0)
HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(0)
};
const int HYUNDAI_PARAM_CANFD_ALT_BUTTONS = 32;
const int HYUNDAI_PARAM_CANFD_HDA2_ALT_STEERING = 128;
bool hyundai_canfd_alt_buttons = false;
bool hyundai_canfd_hda2_alt_steering = false;
bool hyundai_canfd_buffered_fwd = false;
int hyundai_canfd_hda2_get_lkas_addr(void) {
return hyundai_canfd_hda2_alt_steering ? 0x110 : 0x50;
}
static uint8_t hyundai_canfd_get_counter(const CANPacket_t *msg) {
static uint8_t hyundai_canfd_get_counter(const CANPacket_t* to_push) {
uint8_t ret = 0;
if (GET_LEN(msg) == 8U) {
ret = msg->data[1] >> 4;
} else {
ret = msg->data[2];
if (GET_LEN(to_push) == 8U) {
ret = GET_BYTE(to_push, 1) >> 4;
}
else {
ret = GET_BYTE(to_push, 2);
}
return ret;
}
static uint32_t hyundai_canfd_get_checksum(const CANPacket_t *msg) {
uint32_t chksum = msg->data[0] | (msg->data[1] << 8);
static uint32_t hyundai_canfd_get_checksum(const CANPacket_t* to_push) {
uint32_t chksum = GET_BYTE(to_push, 0) | (GET_BYTE(to_push, 1) << 8);
return chksum;
}
static void hyundai_canfd_rx_hook(const CANPacket_t *msg) {
const unsigned pt_bus = hyundai_canfd_lka_steering ? 1U : 0U;
const unsigned int scc_bus = hyundai_camera_scc ? 2U : pt_bus;
typedef struct {
int addr;
int bus; // forwarding block <20><><EFBFBD> tx bus: 0 or 2
int hz;
uint32_t timeout_us;
uint32_t last_tx_us;
} CanfdTxState;
if (msg->bus == pt_bus) {
// forwarding block<63><6B>: bus 0,2<><32> <20><><EFBFBD>
CanfdTxState canfd_tx_states[] = {
{0x50, 0, 100, 0U, 0U}, // 80: LKAS
{0x51, 0, 100, 0U, 0U}, // 81: ADRV_0x51
{0x110, 0, 100, 0U, 0U}, // 272: LKAS_ALT
{0x12A, 0, 100, 0U, 0U}, // 298: LFA
{0x160, 0, 50, 0U, 0U}, // 352: ADRV_0x160
{0x161, 0, 20, 0U, 0U}, // 353: ADRV_0x161
{0x162, 0, 20, 0U, 0U}, // 354: CCNC_0x162
{0x1A0, 0, 50, 0U, 0U}, // 416: SCC_CONTROL
{0x1DA, 0, 1, 0U, 0U}, // 474: ADRV_0x1da
{0x1E0, 0, 20, 0U, 0U}, // 480: LFAHDA_CLUSTER
{0x1EA, 0, 20, 0U, 0U}, // 490: ADRV_0x1ea
{0x200, 0, 20, 0U, 0U}, // 512: ADRV_0x200
{0x2A4, 0, 20, 0U, 0U}, // 676: CAM_0x2a4
{0x345, 0, 5, 0U, 0U}, // 837: ADRV_0x345
{0x362, 0, 10, 0U, 0U}, // 866: CAM_0x362
{0x0CB, 0, 100, 0U, 0U}, // 203: LFA_ALT
{0x175, 2, 50, 0U, 0U}, // 373: TCS
{0x1AA, 2, 50, 0U, 0U}, // 426: CRUISE_ALT_BUTTONS
{0x1CF, 2, 50, 0U, 0U}, // 463: CRUISE_BUTTON
{0x1FA, 2, 10, 0U, 0U}, // 506: CLUSTER_SPEED_LIMIT
{0x0EA, 2, 100, 0U, 0U}, // 234: MDPS
{0x2AF, 2, 10, 0U, 0U}, // 687: STEER_TOUCH_2AF
{0x4A3, 2, 5, 0U, 0U}, // 1187: HDA_INFO_4A3
{0x4B4, 2, 10, 0U, 0U}, // 1204: NEW_MSG_4B4
{0x4BE, 2, 10, 0U, 0U}, // 1214: NEW_MSG_4BE
{0x4B9, 2, 10, 0U, 0U}, // 1209: NEW_MSG_4B9
{0, 0, 0, 0U, 0U},
};
static CanfdTxState* find_canfd_tx_state(int bus, int addr) {
for (int i = 0; canfd_tx_states[i].addr > 0; i++) {
if ((canfd_tx_states[i].addr == addr) && (canfd_tx_states[i].bus == bus)) {
return &canfd_tx_states[i];
}
}
return NULL;
}
static void hyundai_canfd_set_counter(CANPacket_t* to_push, uint8_t counter) {
if (GET_LEN(to_push) == 8U) {
to_push->data[1] = (to_push->data[1] & 0x0FU) | ((counter & 0x0FU) << 4);
}
else {
to_push->data[2] = counter;
}
}
static void hyundai_canfd_set_checksum(CANPacket_t* to_push, uint16_t checksum) {
to_push->data[0] = (uint8_t)(checksum & 0xFFU);
to_push->data[1] = (uint8_t)((checksum >> 8U) & 0xFFU);
}
static void hyundai_canfd_update_checksum(CANPacket_t* to_push) {
to_push->data[0] = 0U;
to_push->data[1] = 0U;
uint32_t checksum = hyundai_common_canfd_compute_checksum(to_push);
hyundai_canfd_set_checksum(to_push, (uint16_t)checksum);
}
static void canfd_apply_counter_and_update_checksum(CANPacket_t* dst, uint8_t counter) {
hyundai_canfd_set_counter(dst, counter);
hyundai_canfd_update_checksum(dst);
}
static void canfd_record_tx_time(int bus, int addr, bool tx) {
CanfdTxState* st = find_canfd_tx_state(bus, addr);
if (st != NULL) {
st->last_tx_us = tx ? microsecond_timer_get() : 0U;
}
}
static bool canfd_should_block_fwd(int tx_bus, int addr, uint32_t now) {
CanfdTxState* st = find_canfd_tx_state(tx_bus, addr);
if (st == NULL) {
return false;
}
return (now - st->last_tx_us) < st->timeout_us;
}
#define CANFD_BFWD_MAX_QUEUE 2
#define CANFD_BFWD_REUSE_MAX 2
typedef struct {
int addr;
int dst_bus;
bool enabled;
bool started;
uint8_t head;
uint8_t tail;
uint8_t count;
uint8_t reuse_left;
bool has_last_pkt;
CANPacket_t last_pkt;
CANPacket_t q[CANFD_BFWD_MAX_QUEUE];
} CanfdBufferedFwd;
CanfdBufferedFwd canfd_bfwd[] = {
{.addr = 0x1A0, .dst_bus = 0, .enabled = true }, // SCC_CONTROL
{.addr = 0x12A, .dst_bus = 0, .enabled = true }, // LFA
{.addr = 0x0CB, .dst_bus = 0, .enabled = true }, // LFA_ALT
{.addr = 0x0EA, .dst_bus = 2, .enabled = true }, // MDPS
{.addr = 0x1AA, .dst_bus = 2, .enabled = true }, // CRUISE_ALT_BUTTONS
// {.addr = 0x1CF, .dst_bus = 2, .enabled = true }, // CRUISE_BUTTON
{.addr = 0x175, .dst_bus = 2, .enabled = true }, // TCS
{ 0 },
};
static void canfd_copy_packet(CANPacket_t* dst, const CANPacket_t* src) {
dst->fd = src->fd;
dst->returned = 0U;
dst->rejected = 0U;
dst->extended = src->extended;
dst->addr = src->addr;
dst->bus = src->bus;
dst->data_len_code = src->data_len_code;
for (uint8_t i = 0U; i < GET_LEN(src); i++) {
dst->data[i] = src->data[i];
}
}
static CanfdBufferedFwd* canfd_bfwd_find(int addr, int dst_bus) {
for (int i = 0; canfd_bfwd[i].addr > 0; i++) {
if (canfd_bfwd[i].enabled &&
(canfd_bfwd[i].addr == addr) &&
(canfd_bfwd[i].dst_bus == dst_bus)) {
return &canfd_bfwd[i];
}
}
return NULL;
}
static void canfd_bfwd_reset(CanfdBufferedFwd* st) {
st->started = false;
st->head = 0U;
st->tail = 0U;
st->count = 0U;
st->reuse_left = 0U;
st->has_last_pkt = false;
st->last_pkt = (CANPacket_t){0};
}
static void canfd_bfwd_push(CanfdBufferedFwd* st, const CANPacket_t* pkt) {
if ((st == NULL) || !st->enabled) return;
if (GET_BUS(pkt) != st->dst_bus) return;
// queue<75><65> <20>̹<EFBFBD> 2<><32><EFBFBD><EFBFBD> <20>̹<EFBFBD> <20><> packet<65><74> <20><><EFBFBD><EFBFBD>
if (st->count >= CANFD_BFWD_MAX_QUEUE) {
return;
}
canfd_copy_packet(&st->q[st->tail], pkt);
st->tail = (st->tail + 1U) % CANFD_BFWD_MAX_QUEUE;
st->count++;
st->started = true;
}
static bool canfd_bfwd_pop(CanfdBufferedFwd* st, CANPacket_t* pkt) {
if ((st == NULL) || !st->enabled) {
return false;
}
if (!st->started || (st->count == 0U)) {
return false;
}
canfd_copy_packet(pkt, &st->q[st->head]);
st->head = (st->head + 1U) % CANFD_BFWD_MAX_QUEUE;
st->count--;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> packet <20><><EFBFBD><EFBFBD>
canfd_copy_packet(&st->last_pkt, pkt);
st->has_last_pkt = true;
st->reuse_left = CANFD_BFWD_REUSE_MAX;
if (st->count == 0U) {
st->started = false;
}
return true;
}
static bool canfd_bfwd_reuse_last(CanfdBufferedFwd* st, CANPacket_t* pkt) {
if ((st == NULL) || !st->enabled) {
return false;
}
if (!st->has_last_pkt || (st->reuse_left == 0U)) {
return false;
}
canfd_copy_packet(pkt, &st->last_pkt);
st->reuse_left--;
return true;
}
static void hyundai_canfd_rx_hook(const CANPacket_t *to_push) {
int bus = GET_BUS(to_push);
int addr = GET_ADDR(to_push);
int pt_bus = hyundai_canfd_hda2 ? 1 : 0;
const int scc_bus = hyundai_camera_scc ? 2 : pt_bus;
if (hyundai_camera_scc) pt_bus = 0;
if (bus == pt_bus) {
// driver torque
if (msg->addr == 0xeaU) {
int torque_driver_new = ((msg->data[11] & 0x1fU) << 8U) | msg->data[10];
if (addr == 0xea) {
int torque_driver_new = ((GET_BYTE(to_push, 11) & 0x1fU) << 8U) | GET_BYTE(to_push, 10);
torque_driver_new -= 4095;
update_sample(&torque_driver, torque_driver_new);
}
// cruise buttons
const unsigned int button_addr = hyundai_canfd_alt_buttons ? 0x1aaU : 0x1cfU;
if (msg->addr == button_addr) {
const int button_addr = hyundai_canfd_alt_buttons ? 0x1aa : 0x1cf;
if (addr == button_addr) {
bool main_button = false;
int cruise_button = 0;
if (msg->addr == 0x1cfU) {
cruise_button = msg->data[2] & 0x7U;
main_button = GET_BIT(msg, 19U);
aol_button_press = GET_BIT(msg, 23U) ? AOL_BUTTON_PRESSED : AOL_BUTTON_NOT_PRESSED;
if (addr == 0x1cf) {
cruise_button = GET_BYTE(to_push, 2) & 0x7U;
main_button = GET_BIT(to_push, 19U);
} else {
cruise_button = (msg->data[4] >> 4) & 0x7U;
main_button = GET_BIT(msg, 34U);
aol_button_press = GET_BIT(msg, 39U) ? AOL_BUTTON_PRESSED : AOL_BUTTON_NOT_PRESSED;
cruise_button = (GET_BYTE(to_push, 4) >> 4) & 0x7U;
main_button = GET_BIT(to_push, 34U);
}
hyundai_common_cruise_buttons_check(cruise_button, main_button);
}
// gas press, different for EV, hybrid, and ICE models
if ((msg->addr == 0x35U) && hyundai_ev_gas_signal) {
gas_pressed = msg->data[5] != 0U;
} else if ((msg->addr == 0x105U) && hyundai_hybrid_gas_signal) {
gas_pressed = GET_BIT(msg, 103U) || (msg->data[13] != 0U) || GET_BIT(msg, 112U);
} else if ((msg->addr == 0x100U) && !hyundai_ev_gas_signal && !hyundai_hybrid_gas_signal) {
gas_pressed = GET_BIT(msg, 176U);
if ((addr == 0x35) && hyundai_ev_gas_signal) {
gas_pressed = GET_BYTE(to_push, 5) != 0U;
} else if ((addr == 0x105) && hyundai_hybrid_gas_signal) {
gas_pressed = GET_BIT(to_push, 103U) || (GET_BYTE(to_push, 13) != 0U) || GET_BIT(to_push, 112U);
} else if ((addr == 0x100) && !hyundai_ev_gas_signal && !hyundai_hybrid_gas_signal) {
gas_pressed = GET_BIT(to_push, 176U);
} else {
}
// brake press
if (msg->addr == 0x175U) {
brake_pressed = GET_BIT(msg, 81U);
if (addr == 0x175) {
brake_pressed = GET_BIT(to_push, 81U);
}
// vehicle moving
if (msg->addr == 0xa0U) {
uint32_t fl = (GET_BYTES(msg, 8, 2)) & 0x3FFFU;
uint32_t fr = (GET_BYTES(msg, 10, 2)) & 0x3FFFU;
uint32_t rl = (GET_BYTES(msg, 12, 2)) & 0x3FFFU;
uint32_t rr = (GET_BYTES(msg, 14, 2)) & 0x3FFFU;
if (addr == 0xa0) {
uint32_t fl = (GET_BYTES(to_push, 8, 2)) & 0x3FFFU;
uint32_t fr = (GET_BYTES(to_push, 10, 2)) & 0x3FFFU;
uint32_t rl = (GET_BYTES(to_push, 12, 2)) & 0x3FFFU;
uint32_t rr = (GET_BYTES(to_push, 14, 2)) & 0x3FFFU;
vehicle_moving = (fl > HYUNDAI_STANDSTILL_THRSLD) || (fr > HYUNDAI_STANDSTILL_THRSLD) ||
(rl > HYUNDAI_STANDSTILL_THRSLD) || (rr > HYUNDAI_STANDSTILL_THRSLD);
// average of all 4 wheel speeds. Conversion: raw * 0.03125 / 3.6 = m/s
UPDATE_VEHICLE_SPEED((fr + rr + rl + fl) / 4.0 * 0.03125 * KPH_TO_MS);
UPDATE_VEHICLE_SPEED((fr + rr + rl + fl) / 4.0 * 0.03125 / 3.6);
}
}
if (msg->bus == scc_bus) {
if (bus == scc_bus) {
// cruise state
if ((msg->addr == 0x1a0U) && !hyundai_longitudinal) {
if ((addr == 0x1a0) && !hyundai_longitudinal) {
// 1=enabled, 2=driver override
int cruise_status = ((msg->data[8] >> 4) & 0x7U);
int cruise_status = ((GET_BYTE(to_push, 8) >> 4) & 0x7U);
bool cruise_engaged = (cruise_status == 1) || (cruise_status == 2);
hyundai_common_cruise_state_check(cruise_engaged);
acc_main_on = GET_BIT(msg, 66U);
}
}
hyundai_common_reset_acc_main_on_mismatches();
const int steer_addr = hyundai_canfd_hda2 ? hyundai_canfd_hda2_get_lkas_addr() : 0x12a;
bool stock_ecu_detected = (addr == steer_addr) && (bus == 0);
if (hyundai_longitudinal) {
// on HDA2, ensure ADRV ECU is still knocked out
// on others, ensure accel msg is blocked from camera
const int stock_scc_bus = hyundai_canfd_hda2 ? 1 : 0;
stock_ecu_detected = stock_ecu_detected || ((addr == 0x1a0) && (bus == stock_scc_bus));
}
generic_rx_checks();
stock_ecu_check(stock_ecu_detected);
}
static bool hyundai_canfd_tx_hook(const CANPacket_t *msg) {
static bool hyundai_canfd_tx_hook(const CANPacket_t *to_send_const) {
CANPacket_t* to_send = (CANPacket_t*)to_send_const;
const TorqueSteeringLimits HYUNDAI_CANFD_STEERING_LIMITS = {
.max_torque = 270,
.max_torque = 512,
.max_rt_delta = 112,
.max_rate_up = 2,
.max_rate_down = 3,
.max_rate_up = 10,
.max_rate_down = 10,
.driver_torque_allowance = 250,
.driver_torque_multiplier = 2,
.type = TorqueDriverLimited,
@@ -159,12 +560,14 @@ static bool hyundai_canfd_tx_hook(const CANPacket_t *msg) {
};
bool tx = true;
int addr = GET_ADDR(to_send);
bool violation = false;
// steering
const unsigned int steer_addr = (hyundai_canfd_lka_steering && !hyundai_longitudinal) ? hyundai_canfd_get_lka_addr() : 0x12aU;
if (msg->addr == steer_addr) {
int desired_torque = (((msg->data[6] & 0xFU) << 7U) | (msg->data[5] >> 1U)) - 1024U;
bool steer_req = GET_BIT(msg, 52U);
const int steer_addr = (hyundai_canfd_hda2 && !hyundai_longitudinal) ? hyundai_canfd_hda2_get_lkas_addr() : 0x12a;
if (addr == steer_addr) {
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_CANFD_STEERING_LIMITS)) {
tx = false;
@@ -172,209 +575,166 @@ static bool hyundai_canfd_tx_hook(const CANPacket_t *msg) {
}
// cruise buttons check
if (msg->addr == 0x1cfU) {
int button = msg->data[2] & 0x7U;
if (addr == 0x1cf) {
int button = GET_BYTE(to_send, 2) & 0x7U;
bool is_cancel = (button == HYUNDAI_BTN_CANCEL);
bool is_resume = (button == HYUNDAI_BTN_RESUME);
bool is_set = (button == HYUNDAI_BTN_SET);
bool allowed = (is_cancel && cruise_engaged_prev) || ((is_resume || is_set) && controls_allowed);
bool allowed = (is_cancel && (cruise_engaged_prev || controls_allowed)) || (is_resume && controls_allowed);
if (!allowed) {
tx = false;
}
}
// UDS: only tester present ("\x02\x3E\x80\x00\x00\x00\x00\x00") allowed on diagnostics address
if (((msg->addr == 0x730U) && hyundai_canfd_lka_steering) || ((msg->addr == 0x7D0U) && !hyundai_camera_scc)) {
if ((GET_BYTES(msg, 0, 4) != 0x00803E02U) || (GET_BYTES(msg, 4, 4) != 0x0U)) {
if ((addr == 0x730) && hyundai_canfd_hda2) {
if ((GET_BYTES(to_send, 0, 4) != 0x00803E02U) || (GET_BYTES(to_send, 4, 4) != 0x0U)) {
tx = false;
}
}
// ACCEL: safety check
if (msg->addr == 0x1a0U) {
int desired_accel_raw = (((msg->data[17] & 0x7U) << 8) | msg->data[16]) - 1023U;
int desired_accel_val = ((msg->data[18] << 4) | (msg->data[17] >> 4)) - 1023U;
if (addr == 0x1a0) {
int desired_accel_raw = (((GET_BYTE(to_send, 17) & 0x7U) << 8) | GET_BYTE(to_send, 16)) - 1023U;
int desired_accel_val = ((GET_BYTE(to_send, 18) << 4) | (GET_BYTE(to_send, 17) >> 4)) - 1023U;
bool violation = false;
if (hyundai_longitudinal) {
violation |= longitudinal_accel_checks(desired_accel_raw, HYUNDAI_LONG_LIMITS);
violation |= longitudinal_accel_checks(desired_accel_val, HYUNDAI_LONG_LIMITS);
} else {
}
else {
// only used to cancel on here
const int acc_mode = (msg->data[8] >> 4) & 0x7U;
const int acc_mode = (GET_BYTE(to_send, 8) >> 4) & 0x7U;
if (acc_mode != 4) {
violation = true;
}
if ((desired_accel_raw != 0) || (desired_accel_val != 0)) {
violation = true;
}
}
if (violation) {
tx = false;
}
acc_main_on_tx = GET_BIT(msg, 66U);
hyundai_common_acc_main_on_sync();
}
if (violation) {
tx = false;
}
else if (hyundai_canfd_buffered_fwd) {
CanfdBufferedFwd* bfwd = canfd_bfwd_find(addr, GET_BUS(to_send));
if (bfwd != NULL) {
canfd_bfwd_push(bfwd, to_send);
//tx = false;
return true;
}
}
canfd_record_tx_time(GET_BUS(to_send), addr, tx);
return tx;
}
static bool hyundai_canfd_fwd_hook(int bus_num, int addr) {
uint32_t now = microsecond_timer_get();
if (bus_num == 0) {
if (canfd_should_block_fwd(2, addr, now)) {
return true;
}
if (addr == 0x4B9) {
return true;
}
return false;
}
return (bus_num != 2) || canfd_should_block_fwd(0, addr, now);
}
static safety_config hyundai_canfd_init(uint16_t param) {
const uint16_t HYUNDAI_PARAM_CANFD_LKA_STEERING_ALT = 128;
const uint16_t HYUNDAI_PARAM_CANFD_ALT_BUTTONS = 32;
static const CanMsg HYUNDAI_CANFD_LKA_STEERING_TX_MSGS[] = {
HYUNDAI_CANFD_LKA_STEERING_COMMON_TX_MSGS(0, 1)
};
for (int i = 0; canfd_tx_states[i].addr > 0; i++) {
canfd_tx_states[i].timeout_us = (uint32_t)(1000000.0 / canfd_tx_states[i].hz) + 20000U;
canfd_tx_states[i].last_tx_us = 0U;
}
static const CanMsg HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS[] = {
HYUNDAI_CANFD_LKA_STEERING_ALT_COMMON_TX_MSGS(0, 1)
};
static const CanMsg HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS[] = {
HYUNDAI_CANFD_LKA_STEERING_COMMON_TX_MSGS(0, 1)
HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(1)
HYUNDAI_CANFD_SCC_CONTROL_COMMON_TX_MSGS(1, true)
{0x51, 0, 32, .check_relay = false}, // ADRV_0x51
{0x730, 1, 8, .check_relay = false}, // tester present for ADAS ECU disable
{0x160, 1, 16, .check_relay = false}, // ADRV_0x160
{0x1EA, 1, 32, .check_relay = false}, // ADRV_0x1ea
{0x200, 1, 8, .check_relay = false}, // ADRV_0x200
{0x345, 1, 8, .check_relay = false}, // ADRV_0x345
{0x1DA, 1, 32, .check_relay = false}, // ADRV_0x1da
};
static const CanMsg HYUNDAI_CANFD_LFA_STEERING_TX_MSGS[] = {
HYUNDAI_CANFD_CRUISE_BUTTON_TX_MSGS(2)
HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(0)
HYUNDAI_CANFD_SCC_CONTROL_COMMON_TX_MSGS(0, false)
};
// ADRV_0x160 is checked for radar liveness
static const CanMsg HYUNDAI_CANFD_LFA_STEERING_LONG_TX_MSGS[] = {
HYUNDAI_CANFD_CRUISE_BUTTON_TX_MSGS(2)
HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(0)
HYUNDAI_CANFD_SCC_CONTROL_COMMON_TX_MSGS(0, true)
{0x160, 0, 16, .check_relay = true}, // ADRV_0x160
{0x7D0, 0, 8, .check_relay = false}, // tester present for radar ECU disable
};
// ADRV_0x160 is checked for relay malfunction
#define HYUNDAI_CANFD_LFA_STEERING_CAMERA_SCC_TX_MSGS(longitudinal) \
HYUNDAI_CANFD_CRUISE_BUTTON_TX_MSGS(2) \
HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(0) \
HYUNDAI_CANFD_SCC_CONTROL_COMMON_TX_MSGS(0, (longitudinal)) \
{0x160, 0, 16, .check_relay = (longitudinal)}, /* ADRV_0x160 */ \
for (int i = 0; canfd_bfwd[i].addr > 0; i++) {
canfd_bfwd_reset(&canfd_bfwd[i]);
}
hyundai_common_init(param);
gen_crc_lookup_table_16(0x1021, hyundai_canfd_crc_lut);
hyundai_canfd_alt_buttons = GET_FLAG(param, HYUNDAI_PARAM_CANFD_ALT_BUTTONS);
hyundai_canfd_lka_steering_alt = GET_FLAG(param, HYUNDAI_PARAM_CANFD_LKA_STEERING_ALT);
hyundai_canfd_hda2_alt_steering = GET_FLAG(param, HYUNDAI_PARAM_CANFD_HDA2_ALT_STEERING);
hyundai_canfd_buffered_fwd = hyundai_camera_scc;
// no long for radar-SCC HDA1 yet
//if (!hyundai_canfd_hda2 && !hyundai_camera_scc) {
// hyundai_longitudinal = false;
//}
safety_config ret;
if (hyundai_longitudinal) {
if (hyundai_canfd_lka_steering) {
static RxCheck hyundai_canfd_lka_steering_long_rx_checks[] = {
HYUNDAI_CANFD_STD_BUTTONS_RX_CHECKS(1)
};
ret = BUILD_SAFETY_CFG(hyundai_canfd_lka_steering_long_rx_checks, HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS);
if (hyundai_canfd_hda2) {
if (hyundai_canfd_alt_buttons) { // carrot : for CANIVAL 4TH HDA2
if (hyundai_camera_scc) ret = BUILD_SAFETY_CFG(hyundai_canfd_hda2_long_alt_buttons_rx_checks_scc2, HYUNDAI_CANFD_HDA2_LONG_TX_MSGS);
else ret = BUILD_SAFETY_CFG(hyundai_canfd_hda2_long_alt_buttons_rx_checks, HYUNDAI_CANFD_HDA2_LONG_TX_MSGS);
}
else {
if (hyundai_camera_scc) ret = BUILD_SAFETY_CFG(hyundai_canfd_hda2_long_rx_checks_scc2, HYUNDAI_CANFD_HDA2_LONG_TX_MSGS);
else ret = BUILD_SAFETY_CFG(hyundai_canfd_hda2_long_rx_checks, HYUNDAI_CANFD_HDA2_LONG_TX_MSGS);
}
} else {
// Longitudinal checks for LFA steering
static RxCheck hyundai_canfd_long_rx_checks[] = {
HYUNDAI_CANFD_STD_BUTTONS_RX_CHECKS(0)
};
static RxCheck hyundai_canfd_alt_buttons_long_rx_checks[] = {
HYUNDAI_CANFD_ALT_BUTTONS_RX_CHECKS(0)
};
static CanMsg hyundai_canfd_lfa_steering_camera_scc_tx_msgs[] = {
HYUNDAI_CANFD_LFA_STEERING_CAMERA_SCC_TX_MSGS(true)
};
if (hyundai_canfd_alt_buttons) {
SET_RX_CHECKS(hyundai_canfd_alt_buttons_long_rx_checks, ret);
} else {
SET_RX_CHECKS(hyundai_canfd_long_rx_checks, ret);
}
if (hyundai_camera_scc) {
SET_TX_MSGS(hyundai_canfd_lfa_steering_camera_scc_tx_msgs, ret);
} else {
SET_TX_MSGS(HYUNDAI_CANFD_LFA_STEERING_LONG_TX_MSGS, ret);
}
ret = hyundai_canfd_alt_buttons ? BUILD_SAFETY_CFG(hyundai_canfd_long_alt_buttons_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS) : \
BUILD_SAFETY_CFG(hyundai_canfd_long_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS);
}
} else {
if (hyundai_canfd_lka_steering) {
// *** LKA steering checks ***
// E-CAN is on bus 1, SCC messages are sent on cars with ADRV ECU.
// Does not use the alt buttons message
static RxCheck hyundai_canfd_lka_steering_rx_checks[] = {
HYUNDAI_CANFD_STD_BUTTONS_RX_CHECKS(1)
HYUNDAI_CANFD_SCC_ADDR_CHECK(1)
if (hyundai_canfd_hda2 && hyundai_camera_scc) {
if (hyundai_canfd_alt_buttons) { // carrot : for CANIVAL 4TH HDA2
ret = hyundai_canfd_hda2_alt_steering ? BUILD_SAFETY_CFG(hyundai_canfd_hda2_alt_buttons_rx_checks_scc2, HYUNDAI_CANFD_HDA2_LONG_TX_MSGS) : \
BUILD_SAFETY_CFG(hyundai_canfd_hda2_alt_buttons_rx_checks_scc2, HYUNDAI_CANFD_HDA2_LONG_TX_MSGS);
}
else {
ret = hyundai_canfd_hda2_alt_steering ? BUILD_SAFETY_CFG(hyundai_canfd_hda2_rx_checks_scc2, HYUNDAI_CANFD_HDA2_LONG_TX_MSGS) : \
BUILD_SAFETY_CFG(hyundai_canfd_hda2_rx_checks_scc2, HYUNDAI_CANFD_HDA2_LONG_TX_MSGS);
}
}else if (hyundai_canfd_hda2) {
if (hyundai_canfd_alt_buttons) { // carrot : for CANIVAL 4TH HDA2
ret = hyundai_canfd_hda2_alt_steering ? BUILD_SAFETY_CFG(hyundai_canfd_hda2_alt_buttons_rx_checks, HYUNDAI_CANFD_HDA2_LONG_TX_MSGS) : \
BUILD_SAFETY_CFG(hyundai_canfd_hda2_alt_buttons_rx_checks, HYUNDAI_CANFD_HDA2_LONG_TX_MSGS);
}
else {
ret = hyundai_canfd_hda2_alt_steering ? BUILD_SAFETY_CFG(hyundai_canfd_hda2_rx_checks, HYUNDAI_CANFD_HDA2_LONG_TX_MSGS) : \
BUILD_SAFETY_CFG(hyundai_canfd_hda2_rx_checks, HYUNDAI_CANFD_HDA2_LONG_TX_MSGS);
}
} else if (!hyundai_camera_scc) {
static RxCheck hyundai_canfd_radar_scc_alt_buttons_rx_checks[] = {
HYUNDAI_CANFD_COMMON_RX_CHECKS(0)
HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(0)
HYUNDAI_CANFD_SCC_ADDR_CHECK(0)
};
SET_RX_CHECKS(hyundai_canfd_lka_steering_rx_checks, ret);
if (hyundai_canfd_lka_steering_alt) {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS, ret);
} else {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_TX_MSGS, ret);
}
} else if (!hyundai_camera_scc) {
// Radar sends SCC messages on these cars instead of camera
static RxCheck hyundai_canfd_radar_scc_rx_checks[] = {
HYUNDAI_CANFD_STD_BUTTONS_RX_CHECKS(0)
HYUNDAI_CANFD_COMMON_RX_CHECKS(0)
HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(0)
HYUNDAI_CANFD_SCC_ADDR_CHECK(0)
};
static RxCheck hyundai_canfd_alt_buttons_radar_scc_rx_checks[] = {
HYUNDAI_CANFD_ALT_BUTTONS_RX_CHECKS(0)
HYUNDAI_CANFD_SCC_ADDR_CHECK(0)
};
SET_TX_MSGS(HYUNDAI_CANFD_LFA_STEERING_TX_MSGS, ret);
if (hyundai_canfd_alt_buttons) {
SET_RX_CHECKS(hyundai_canfd_alt_buttons_radar_scc_rx_checks, ret);
} else {
SET_RX_CHECKS(hyundai_canfd_radar_scc_rx_checks, ret);
}
ret = hyundai_canfd_alt_buttons ? BUILD_SAFETY_CFG(hyundai_canfd_radar_scc_alt_buttons_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS) : \
BUILD_SAFETY_CFG(hyundai_canfd_radar_scc_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS);
} else {
// *** LFA steering checks ***
// Camera sends SCC messages on LFA steering cars.
// *** Non-HDA2 checks ***
static RxCheck hyundai_canfd_alt_buttons_rx_checks[] = {
HYUNDAI_CANFD_COMMON_RX_CHECKS(0)
HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(0)
HYUNDAI_CANFD_SCC_ADDR_CHECK(2)
};
// Camera sends SCC messages on HDA1.
// Both button messages exist on some platforms, so we ensure we track the correct one using flag
static RxCheck hyundai_canfd_rx_checks[] = {
HYUNDAI_CANFD_STD_BUTTONS_RX_CHECKS(0)
HYUNDAI_CANFD_COMMON_RX_CHECKS(0)
HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(0)
HYUNDAI_CANFD_SCC_ADDR_CHECK(2)
};
static RxCheck hyundai_canfd_alt_buttons_rx_checks[] = {
HYUNDAI_CANFD_ALT_BUTTONS_RX_CHECKS(0)
HYUNDAI_CANFD_SCC_ADDR_CHECK(2)
};
static CanMsg hyundai_canfd_lfa_steering_camera_scc_tx_msgs[] = {
HYUNDAI_CANFD_LFA_STEERING_CAMERA_SCC_TX_MSGS(false)
};
SET_TX_MSGS(hyundai_canfd_lfa_steering_camera_scc_tx_msgs, ret);
if (hyundai_canfd_alt_buttons) {
SET_RX_CHECKS(hyundai_canfd_alt_buttons_rx_checks, ret);
} else {
SET_RX_CHECKS(hyundai_canfd_rx_checks, ret);
}
ret = hyundai_canfd_alt_buttons ? BUILD_SAFETY_CFG(hyundai_canfd_alt_buttons_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS) : \
BUILD_SAFETY_CFG(hyundai_canfd_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS);
}
}
@@ -385,6 +745,7 @@ const safety_hooks hyundai_canfd_hooks = {
.init = hyundai_canfd_init,
.rx = hyundai_canfd_rx_hook,
.tx = hyundai_canfd_tx_hook,
.fwd = hyundai_canfd_fwd_hook,
.get_counter = hyundai_canfd_get_counter,
.get_checksum = hyundai_canfd_get_checksum,
.compute_checksum = hyundai_common_canfd_compute_checksum,

View File

@@ -2,11 +2,20 @@
#include "iqdbc/safety/declarations.h"
#define GET_ADDR(msg) ((msg)->addr)
#define GET_BUS(msg) ((msg)->bus)
#define GET_BYTE(msg, b) ((msg)->data[(b)])
static int cruise_main_prev = 0;
static void generic_rx_checks(void);
static void stock_ecu_check(bool stock_ecu_detected);
extern uint16_t hyundai_canfd_crc_lut[256];
uint16_t hyundai_canfd_crc_lut[256];
static const uint8_t HYUNDAI_PREV_BUTTON_SAMPLES = 8; // roughly 160 ms
//
extern const uint32_t HYUNDAI_STANDSTILL_THRSLD;
const uint32_t HYUNDAI_STANDSTILL_THRSLD = 12; // 0.375 kph
@@ -17,13 +26,6 @@ enum {
HYUNDAI_BTN_CANCEL = 4,
};
enum {
HYUNDAI_PARAM_IQ_ESCC = 16,
HYUNDAI_PARAM_IQ_MAIN_BTN_LONG_TOGGLE = 32,
HYUNDAI_PARAM_IQ_HAS_LDA_BUTTON = 64,
HYUNDAI_PARAM_IQ_NON_SCC = 128,
};
// common state
extern bool hyundai_ev_gas_signal;
bool hyundai_ev_gas_signal = false;
@@ -37,8 +39,8 @@ bool hyundai_longitudinal = false;
extern bool hyundai_camera_scc;
bool hyundai_camera_scc = false;
extern bool hyundai_canfd_lka_steering;
bool hyundai_canfd_lka_steering = false;
extern bool hyundai_canfd_hda2;
bool hyundai_canfd_hda2 = false;
extern bool hyundai_alt_limits;
bool hyundai_alt_limits = false;
@@ -49,57 +51,29 @@ bool hyundai_fcev_gas_signal = false;
extern bool hyundai_alt_limits_2;
bool hyundai_alt_limits_2 = false;
// ESCC
extern bool hyundai_escc;
bool hyundai_escc = false;
extern bool hyundai_main_btn_long_toggle;
bool hyundai_main_btn_long_toggle = false;
extern bool hyundai_has_lda_button;
bool hyundai_has_lda_button = false;
extern bool hyundai_non_scc;
bool hyundai_non_scc = false;
static uint8_t hyundai_last_button_interaction; // button messages since the user pressed an enable button
static bool main_button_prev;
static bool acc_main_on_prev;
static bool acc_main_on_tx;
static uint32_t acc_main_on_mismatches;
void hyundai_common_init(uint16_t param) {
const uint16_t HYUNDAI_PARAM_EV_GAS = 1;
const uint16_t HYUNDAI_PARAM_HYBRID_GAS = 2;
const uint16_t HYUNDAI_PARAM_CAMERA_SCC = 8;
const uint16_t HYUNDAI_PARAM_CANFD_LKA_STEERING = 16;
const uint16_t HYUNDAI_PARAM_ALT_LIMITS = 64; // TODO: shift this down with the rest of the common flags
const uint16_t HYUNDAI_PARAM_FCEV_GAS = 256;
const uint16_t HYUNDAI_PARAM_ALT_LIMITS_2 = 512;
const int HYUNDAI_PARAM_EV_GAS = 1;
const int HYUNDAI_PARAM_HYBRID_GAS = 2;
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_FCEV_GAS = 256;
const int HYUNDAI_PARAM_ALT_LIMITS_2 = 512;
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_lka_steering = GET_FLAG(param, HYUNDAI_PARAM_CANFD_LKA_STEERING);
hyundai_canfd_hda2 = GET_FLAG(param, HYUNDAI_PARAM_CANFD_HDA2);
hyundai_alt_limits = GET_FLAG(param, HYUNDAI_PARAM_ALT_LIMITS);
hyundai_fcev_gas_signal = GET_FLAG(param, HYUNDAI_PARAM_FCEV_GAS);
hyundai_alt_limits_2 = GET_FLAG(param, HYUNDAI_PARAM_ALT_LIMITS_2);
hyundai_escc = GET_FLAG(current_safety_param_iq, HYUNDAI_PARAM_IQ_ESCC);
hyundai_main_btn_long_toggle = GET_FLAG(current_safety_param_iq, HYUNDAI_PARAM_IQ_MAIN_BTN_LONG_TOGGLE);
hyundai_has_lda_button = GET_FLAG(current_safety_param_iq, HYUNDAI_PARAM_IQ_HAS_LDA_BUTTON);
hyundai_non_scc = GET_FLAG(current_safety_param_iq, HYUNDAI_PARAM_IQ_NON_SCC);
hyundai_last_button_interaction = HYUNDAI_PREV_BUTTON_SAMPLES;
main_button_prev = false;
acc_main_on_prev = false;
acc_main_on_tx = false;
acc_main_on_mismatches = 0U;
#ifdef ALLOW_DEBUG
const uint16_t HYUNDAI_PARAM_LONGITUDINAL = 4;
const int HYUNDAI_PARAM_LONGITUDINAL = 4;
hyundai_longitudinal = GET_FLAG(param, HYUNDAI_PARAM_LONGITUDINAL);
#else
hyundai_longitudinal = false;
@@ -112,19 +86,27 @@ void hyundai_common_cruise_state_check(const bool cruise_engaged) {
// enter controls on rising edge of ACC and recent user button press, exit controls when ACC off
if (!hyundai_longitudinal) {
if (cruise_engaged && !cruise_engaged_prev && (hyundai_last_button_interaction < HYUNDAI_PREV_BUTTON_SAMPLES)) {
hyundai_last_button_interaction = 0U; // carrot
//if (cruise_engaged && !cruise_engaged_prev && (hyundai_last_button_interaction < HYUNDAI_PREV_BUTTON_SAMPLES)) {
if (cruise_engaged) {
controls_allowed = true;
}
if (!cruise_engaged) {
controls_allowed = false;
}
cruise_engaged_prev = cruise_engaged;
}
}
void hyundai_common_cruise_buttons_check(const int cruise_button, const bool main_button) {
if ((cruise_button == HYUNDAI_BTN_RESUME) || (cruise_button == HYUNDAI_BTN_SET) || (cruise_button == HYUNDAI_BTN_CANCEL) || main_button) {
if(main_button && main_button != cruise_main_prev) {
acc_main_on = !acc_main_on;
}
cruise_main_prev = main_button;
if ((cruise_button == HYUNDAI_BTN_RESUME) || (cruise_button == HYUNDAI_BTN_SET) || (cruise_button == HYUNDAI_BTN_CANCEL) ||
(main_button)) {
hyundai_last_button_interaction = 0U;
} else {
hyundai_last_button_interaction = SAFETY_MIN(hyundai_last_button_interaction + 1U, HYUNDAI_PREV_BUTTON_SAMPLES);
@@ -141,66 +123,38 @@ void hyundai_common_cruise_buttons_check(const int cruise_button, const bool mai
// exit controls on cancel press
if (cruise_button == HYUNDAI_BTN_CANCEL) {
controls_allowed = false;
}
// toggle main cruise state on rising edge of main cruise button
if (main_button && !main_button_prev && hyundai_main_btn_long_toggle) {
acc_main_on = !acc_main_on;
}
cruise_button_prev = cruise_button;
main_button_prev = main_button;
}
}
uint32_t hyundai_common_canfd_compute_checksum(const CANPacket_t *msg) {
int len = GET_LEN(msg);
uint32_t address = msg->addr;
uint32_t hyundai_common_canfd_compute_checksum(const CANPacket_t *to_push) {
int len = GET_LEN(to_push);
uint32_t address = GET_ADDR(to_push);
uint16_t crc = 0;
for (int i = 2; i < len; i++) {
crc = (crc << 8U) ^ hyundai_canfd_crc_lut[(crc >> 8U) ^ msg->data[i]];
crc = (crc << 8U) ^ hyundai_canfd_crc_lut[(crc >> 8U) ^ GET_BYTE(to_push, i)];
}
// Add address to crc
crc = (crc << 8U) ^ hyundai_canfd_crc_lut[(crc >> 8U) ^ ((address >> 0U) & 0xFFU)];
crc = (crc << 8U) ^ hyundai_canfd_crc_lut[(crc >> 8U) ^ ((address >> 8U) & 0xFFU)];
if (len == 24) {
if (len == 8) {
crc ^= 0x5f29U;
}
else if (len == 16) {
crc ^= 0x041dU;
}
else if (len == 24) {
crc ^= 0x819dU;
} else if (len == 32) {
}
else if (len == 32) {
crc ^= 0x9f5bU;
} else {
}
return crc;
}
// reset mismatches on rising edge of acc_main_on to avoid rare race conditions when using non-PCM main cruise state
void hyundai_common_reset_acc_main_on_mismatches(void) {
if (acc_main_on && !acc_main_on_prev) {
acc_main_on_mismatches = 0U;
}
acc_main_on_prev = acc_main_on;
}
// exit lateral controls allowed if iqpilot and panda main cruise states are desynced
void hyundai_common_acc_main_on_sync(void) {
if (acc_main_on && !acc_main_on_tx) {
acc_main_on_mismatches += 1U;
if (acc_main_on_mismatches >= 3U) { // desync by 3 frames
acc_main_on = false;
aol_exit_controls(AOL_DISENGAGE_REASON_NON_PCM_ACC_MAIN_DESYNC);
}
} else {
acc_main_on_mismatches = 0U;
}
}
uint32_t get_acc_main_on_mismatches(void) {
return acc_main_on_mismatches;
}

View File

@@ -1,296 +1,46 @@
import unittest
from iqdbc.iqpilot.car.hyundai.values import HyundaiSafetyFlagsIQ
import iqdbc.safety.tests.common as common
from iqdbc.safety.tests.libsafety import libsafety_py
from iqdbc.safety.tests.common import make_msg
class Buttons:
NONE = 0
RESUME = 1
SET = 2
CANCEL = 4
def packet(addr: int, bus: int, length: int, updates: dict[int, int] | None = None):
data = bytearray(length)
for index, value in (updates or {}).items():
data[index] = value
return libsafety_py.make_CANPacket(addr, bus, data)
PREV_BUTTON_SAMPLES = 8
ENABLE_BUTTONS = (Buttons.RESUME, Buttons.SET, Buttons.CANCEL)
def classic_steer(torque: int, request: bool = True):
value = torque + 1024
word = (value << 16) | (int(request) << 27)
return packet(0x340, 0, 8, {i: (word >> (8 * i)) & 0xFF for i in range(4)})
class HyundaiButtonBase:
BUTTONS_TX_BUS = 0 # tx on this bus, rx on 0
SCC_BUS = 0 # rx on this bus
def test_button_sends(self):
"""
RES, SET and CANCEL buttons are allowed
- RES and SET allowed while controls allowed
- CANCEL allowed while cruise is enabled
"""
self.safety.set_controls_allowed(0)
self.assertFalse(self._tx(self._button_msg(Buttons.RESUME, bus=self.BUTTONS_TX_BUS)))
self.assertFalse(self._tx(self._button_msg(Buttons.SET, bus=self.BUTTONS_TX_BUS)))
self.safety.set_controls_allowed(1)
self.assertTrue(self._tx(self._button_msg(Buttons.RESUME, bus=self.BUTTONS_TX_BUS)))
self.assertTrue(self._tx(self._button_msg(Buttons.SET, bus=self.BUTTONS_TX_BUS)))
for enabled in (True, False):
self._rx(self._pcm_status_msg(enabled))
self.assertEqual(enabled, self._tx(self._button_msg(Buttons.CANCEL, bus=self.BUTTONS_TX_BUS)))
def test_enable_control_allowed_from_cruise(self):
"""
Hyundai non-longitudinal only enables on PCM rising edge and recent button press. Tests PCM enabling with:
- disallowed: No buttons
- disallowed: Buttons that don't enable cruise
- allowed: Buttons that do enable cruise
- allowed: Main button with all above combinations
"""
for main_button in (0, 1):
for btn in range(8):
for _ in range(PREV_BUTTON_SAMPLES): # reset
self._rx(self._button_msg(Buttons.NONE))
self._rx(self._pcm_status_msg(False))
self.assertFalse(self.safety.get_controls_allowed())
self._rx(self._button_msg(btn, main_button=main_button))
self._rx(self._pcm_status_msg(True))
controls_allowed = btn in ENABLE_BUTTONS or main_button
self.assertEqual(controls_allowed, self.safety.get_controls_allowed())
def test_sampling_cruise_buttons(self):
"""
Test that we allow controls on recent button press, but not as button leaves sliding window
"""
self._rx(self._button_msg(Buttons.SET))
for i in range(2 * PREV_BUTTON_SAMPLES):
self._rx(self._pcm_status_msg(False))
self.assertFalse(self.safety.get_controls_allowed())
self._rx(self._pcm_status_msg(True))
controls_allowed = i < PREV_BUTTON_SAMPLES
self.assertEqual(controls_allowed, self.safety.get_controls_allowed())
self._rx(self._button_msg(Buttons.NONE))
def canfd_steer(addr: int, length: int, torque: int, request: bool = True):
value = torque + 1024
return packet(addr, 0, length, {
5: (value & 0x7F) << 1,
6: ((value >> 7) & 0xF) | (int(request) << 4),
})
class HyundaiLongitudinalBase(common.LongitudinalAccelSafetyTest):
def classic_accel(accel: int, *, aeb_decel: int = 0, aeb_request: bool = False):
value = accel + 1023
return packet(0x421, 0, 8, {
2: aeb_decel,
3: value & 0xFF,
4: ((value >> 8) & 0x7) | ((value & 0x7) << 5),
5: (value >> 3) & 0xFF,
6: int(aeb_request) << 6,
})
DISABLED_ECU_UDS_MSG: tuple[int, int]
DISABLED_ECU_ACTUATION_MSG: tuple[int, int]
@classmethod
def setUpClass(cls):
if cls.__name__ == "HyundaiLongitudinalBase":
cls.safety = None
raise unittest.SkipTest
def canfd_accel(accel: int, *, acc_mode: int = 0, bus: int = 0):
value = accel + 1023
return packet(0x1A0, bus, 32, {
8: (acc_mode & 0x7) << 4,
16: value & 0xFF,
17: ((value >> 8) & 0x7) | ((value & 0xF) << 4),
18: (value >> 4) & 0xFF,
})
# override these tests from CarSafetyTest, hyundai longitudinal uses button enable
def test_disable_control_allowed_from_cruise(self):
pass
def test_enable_control_allowed_from_cruise(self):
pass
def test_sampling_cruise_buttons(self):
pass
def test_cruise_engaged_prev(self):
pass
def test_button_sends(self):
pass
def _pcm_status_msg(self, enable):
raise Exception
def _accel_msg(self, accel, aeb_req=False, aeb_decel=0):
raise NotImplementedError
def _acc_state_msg(self, enable):
raise NotImplementedError
def _tx_acc_state_msg(self, enable):
raise NotImplementedError
def test_pcm_main_cruise_state_availability(self):
pass
def test_set_resume_buttons(self):
"""
SET and RESUME enter controls allowed on their falling edge.
"""
for btn_prev in range(8):
for btn_cur in range(8):
self._rx(self._button_msg(Buttons.NONE))
self.safety.set_controls_allowed(0)
for _ in range(10):
self._rx(self._button_msg(btn_prev))
self.assertFalse(self.safety.get_controls_allowed())
# should enter controls allowed on falling edge and not transitioning to cancel
should_enable = btn_cur != btn_prev and \
btn_cur != Buttons.CANCEL and \
btn_prev in (Buttons.RESUME, Buttons.SET)
self._rx(self._button_msg(btn_cur))
self.assertEqual(should_enable, self.safety.get_controls_allowed())
def test_cancel_button(self):
self.safety.set_controls_allowed(1)
self._rx(self._button_msg(Buttons.CANCEL))
self.assertFalse(self.safety.get_controls_allowed())
def test_main_cruise_button(self):
"""Test that main cruise button correctly toggles acc_main_on state"""
default_safety_mode = self.safety.get_current_safety_mode()
default_safety_param = self.safety.get_current_safety_param()
default_safety_param_iq = self.safety.get_current_safety_param_iq()
for enable_aol in (True, False):
with self.subTest("enable_aol", aol_enabled=enable_aol):
for main_cruise_toggleable in (True, False):
with self.subTest("main_cruise_toggleable", main_cruise_toggleable=main_cruise_toggleable):
main_cruise_toggleable_flag = HyundaiSafetyFlagsIQ.MAIN_BTN_LONG_TOGGLE if main_cruise_toggleable else 0
self.safety.set_current_safety_param_iq(default_safety_param_iq | main_cruise_toggleable_flag)
self.safety.set_safety_hooks(default_safety_mode, default_safety_param)
# Test initial state
self.safety.set_aol_params(enable_aol, False, False)
self.assertFalse(self.safety.get_acc_main_on())
self._rx(self._main_cruise_button_msg(0))
self._rx(self._main_cruise_button_msg(1))
self.assertEqual(enable_aol and main_cruise_toggleable, self.safety.get_controls_allowed_lat())
self._rx(self._main_cruise_button_msg(0))
self.assertEqual(enable_aol and main_cruise_toggleable, self.safety.get_controls_allowed_lat())
self._rx(self._main_cruise_button_msg(1))
self.assertFalse(self.safety.get_controls_allowed_lat())
for _ in range(10):
self._rx(self._main_cruise_button_msg(1))
self.assertFalse(self.safety.get_controls_allowed_lat())
self.safety.set_current_safety_param_iq(default_safety_param_iq)
def test_acc_main_sync_mismatches_reset(self):
"""Test that acc_main_on_mismatches resets properly on rising edge of main button"""
default_safety_mode = self.safety.get_current_safety_mode()
default_safety_param = self.safety.get_current_safety_param()
default_safety_param_iq = self.safety.get_current_safety_param_iq()
for enable_aol in (True, False):
with self.subTest("enable_aol", aol_enabled=enable_aol):
main_cruise_toggleable_flag = HyundaiSafetyFlagsIQ.MAIN_BTN_LONG_TOGGLE
self.safety.set_current_safety_param_iq(default_safety_param_iq | main_cruise_toggleable_flag)
self.safety.set_safety_hooks(default_safety_mode, default_safety_param)
self.safety.set_aol_params(enable_aol, False, False)
# Initial state
self._rx(self._main_cruise_button_msg(0))
self.assertFalse(self.safety.get_acc_main_on())
# Set up mismatch condition
self._rx(self._main_cruise_button_msg(1)) # Press button - acc_main_on = True
self._rx(self._main_cruise_button_msg(0)) # Release button
self._tx(self._tx_acc_state_msg(False)) # acc_main_on_tx = False
self.assertTrue(self.safety.get_acc_main_on())
self.assertEqual(1, self.safety.get_acc_main_on_mismatches())
# Rising edge of acc_main_on should reset
self._rx(self._main_cruise_button_msg(1)) # Press button again
self._rx(self._main_cruise_button_msg(0)) # Release button
self._tx(self._tx_acc_state_msg(False)) # acc_main_on_tx = False
self.assertFalse(self.safety.get_acc_main_on())
self.assertEqual(0, self.safety.get_acc_main_on_mismatches())
self.safety.set_current_safety_param_iq(default_safety_param_iq)
def test_acc_main_sync_mismatch_counter(self):
"""Test mismatch counter behavior and disengagement"""
default_safety_mode = self.safety.get_current_safety_mode()
default_safety_param = self.safety.get_current_safety_param()
default_safety_param_iq = self.safety.get_current_safety_param_iq()
for enable_aol in (True, False):
with self.subTest("enable_aol", aol_enabled=enable_aol):
main_cruise_toggleable_flag = HyundaiSafetyFlagsIQ.MAIN_BTN_LONG_TOGGLE
self.safety.set_current_safety_param_iq(default_safety_param_iq | main_cruise_toggleable_flag)
self.safety.set_safety_hooks(default_safety_mode, default_safety_param)
self.safety.set_aol_params(enable_aol, False, False)
self.safety.set_controls_allowed_lat(True)
# Start with matched states
self._rx(self._main_cruise_button_msg(0))
self._tx(self._tx_acc_state_msg(False))
self.assertEqual(0, self.safety.get_acc_main_on_mismatches())
# Create mismatch by enabling acc_main_on
self._rx(self._main_cruise_button_msg(1)) # Press button
self._rx(self._main_cruise_button_msg(0)) # Release button
self._tx(self._tx_acc_state_msg(False)) # acc_main_on_tx stays false
self.assertTrue(self.safety.get_acc_main_on())
self.assertEqual(1, self.safety.get_acc_main_on_mismatches())
# Second mismatch
self._tx(self._tx_acc_state_msg(False))
self.assertTrue(self.safety.get_acc_main_on())
self.assertEqual(2, self.safety.get_acc_main_on_mismatches())
# Third mismatch should trigger disengagement
self._tx(self._tx_acc_state_msg(False))
self.assertFalse(self.safety.get_acc_main_on())
self.assertFalse(self.safety.get_controls_allowed_lat())
# Counter should reset after disengagement
self._tx(self._tx_acc_state_msg(False))
self.assertEqual(0, self.safety.get_acc_main_on_mismatches())
self.safety.set_current_safety_param_iq(default_safety_param_iq)
def test_acc_main_sync_mismatch_recovery(self):
default_safety_mode = self.safety.get_current_safety_mode()
default_safety_param = self.safety.get_current_safety_param()
default_safety_param_iq = self.safety.get_current_safety_param_iq()
"""Test that mismatch counter resets when states resync"""
for enable_aol in (True, False):
with self.subTest("enable_aol", aol_enabled=enable_aol):
main_cruise_toggleable_flag = HyundaiSafetyFlagsIQ.MAIN_BTN_LONG_TOGGLE
self.safety.set_current_safety_param_iq(default_safety_param_iq | main_cruise_toggleable_flag)
self.safety.set_safety_hooks(default_safety_mode, default_safety_param)
self.safety.set_aol_params(enable_aol, False, False)
# Create initial mismatch
self._rx(self._main_cruise_button_msg(1)) # Press button
self._rx(self._main_cruise_button_msg(0)) # Release button
self._tx(self._tx_acc_state_msg(False)) # acc_main_on_tx = False
self.assertEqual(1, self.safety.get_acc_main_on_mismatches())
# Sync states
self._tx(self._tx_acc_state_msg(True)) # Match acc_main_on_tx to acc_main_on
self.assertEqual(0, self.safety.get_acc_main_on_mismatches())
self.safety.set_current_safety_param_iq(default_safety_param_iq)
def test_tester_present_allowed(self, ecu_disable: bool = True):
"""
Ensure tester present diagnostic message is allowed to keep ECU knocked out
for longitudinal control.
"""
addr, bus = self.DISABLED_ECU_UDS_MSG
for should_tx, msg in ((True, b"\x02\x3E\x80\x00\x00\x00\x00\x00"),
(False, b"\x03\xAA\xAA\x00\x00\x00\x00\x00")):
tester_present = libsafety_py.make_CANPacket(addr, bus, msg)
self.assertEqual(should_tx and ecu_disable, self._tx(tester_present))
def test_disabled_ecu_alive(self):
"""
If the ECU knockout failed, make sure the relay malfunction is shown
"""
addr, bus = self.DISABLED_ECU_ACTUATION_MSG
self.assertFalse(self.safety.get_relay_malfunction())
self._rx(make_msg(bus, addr, 8))
self.assertTrue(self.safety.get_relay_malfunction())
TESTER_PRESENT = bytes.fromhex("023e800000000000")

664
iqdbc_repo/iqdbc/safety/tests/test_hyundai.py Executable file → Normal file
View File

@@ -1,569 +1,107 @@
#!/usr/bin/env python3
from parameterized import parameterized_class
import random
import unittest
import pytest
from iqdbc.car.hyundai.values import HyundaiSafetyFlags
from iqdbc.car.structs import CarParams
from iqdbc.safety.tests.hyundai_common import TESTER_PRESENT, classic_accel, classic_steer, packet
from iqdbc.safety.tests.libsafety import libsafety_py
import iqdbc.safety.tests.common as common
from iqdbc.safety.tests.common import CANPackerSafety
from iqdbc.safety.tests.hyundai_common import HyundaiButtonBase, HyundaiLongitudinalBase
from iqdbc.iqpilot.car.hyundai.values import HyundaiSafetyFlagsIQ
# LDA button availability
LDA_BUTTON = [
{"SAFETY_PARAM_IQ": HyundaiSafetyFlagsIQ.DEFAULT},
{"SAFETY_PARAM_IQ": HyundaiSafetyFlagsIQ.HAS_LDA_BUTTON},
]
# All combinations of non-SCC HEV/PHEV/EV cars
_ALL_NON_SCC_HEV_EV_COMBOS = [
# Hybrid
{"PCM_STATUS_MSG": ("E_CRUISE_CONTROL", "CRUISE_LAMP_S"),
"ACC_STATE_MSG": ("E_CRUISE_CONTROL", "CRUISE_LAMP_M"),
"GAS_MSG": ("E_EMS11", "CR_Vcu_AccPedDep_Pos"),
"SAFETY_PARAM": HyundaiSafetyFlags.HYBRID_GAS},
# EV
{"PCM_STATUS_MSG": ("LABEL11", "CC_ACT"),
"ACC_STATE_MSG": ("LABEL11", "CC_React"),
"GAS_MSG": ("E_EMS11", "Accel_Pedal_Pos"),
"SAFETY_PARAM": HyundaiSafetyFlags.EV_GAS},
]
ALL_NON_SCC_HEV_EV_COMBOS = [{**p, **lda} for lda in LDA_BUTTON for p in _ALL_NON_SCC_HEV_EV_COMBOS]
# 4 bit checkusm used in some hyundai messages
# lives outside the can packer because we never send this msg
def checksum(msg):
addr, dat, bus = msg
chksum = 0
if addr == 0x386:
for i, b in enumerate(dat):
for j in range(8):
# exclude checksum and counter bits
if (i != 1 or j < 6) and (i != 3 or j < 6) and (i != 5 or j < 6) and (i != 7 or j < 6):
bit = (b >> j) & 1
else:
bit = 0
chksum += bit
chksum = (chksum ^ 9) & 0xF
ret = bytearray(dat)
ret[5] |= (chksum & 0x3) << 6
ret[7] |= (chksum & 0xc) << 4
else:
for i, b in enumerate(dat):
if addr in [0x260, 0x421] and i == 7:
b &= 0x0F if addr == 0x421 else 0xF0
elif addr == 0x394 and i == 6:
b &= 0xF0
elif addr == 0x394 and i == 7:
continue
chksum += sum(divmod(b, 16))
chksum = (16 - chksum) % 16
ret = bytearray(dat)
ret[6 if addr == 0x394 else 7] |= chksum << (4 if addr == 0x421 else 0)
return addr, ret, bus
@parameterized_class(LDA_BUTTON)
class TestHyundaiSafety(HyundaiButtonBase, common.CarSafetyTest, common.DriverTorqueSteeringSafetyTest, common.SteerRequestCutSafetyTest):
TX_MSGS = [[0x340, 0], [0x4F1, 0], [0x485, 0]]
STANDSTILL_THRESHOLD = 12 # 0.375 kph
RELAY_MALFUNCTION_ADDRS = {0: (0x340, 0x485)} # LKAS11
FWD_BLACKLISTED_ADDRS = {2: [0x340, 0x485]}
MAX_RATE_UP = 3
MAX_RATE_DOWN = 7
MAX_TORQUE_LOOKUP = [0], [384]
MAX_RT_DELTA = 112
DRIVER_TORQUE_ALLOWANCE = 50
DRIVER_TORQUE_FACTOR = 2
# Safety around steering req bit
MIN_VALID_STEERING_FRAMES = 89
MAX_INVALID_STEERING_FRAMES = 2
cnt_gas = 0
cnt_speed = 0
cnt_brake = 0
cnt_cruise = 0
cnt_button = 0
SAFETY_PARAM_IQ: int = 0
@classmethod
def setUpClass(cls):
if cls.__name__ == "TestHyundaiSafety":
cls.safety = None
raise unittest.SkipTest
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_current_safety_param_iq(self.SAFETY_PARAM_IQ)
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundai, 0)
self.safety.init_tests()
def _button_msg(self, buttons, main_button=0, bus=0):
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_safety("CLU11", bus, values)
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_safety("EMS16", 0, 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_safety("TCS13", 0, values, fix_checksum=checksum)
def _speed_msg(self, speed):
# safety doesn't scale, so undo the scaling
values = {"WHL_SPD_%s" % s: speed * 0.03125 for s in ["FL", "FR", "RL", "RR"]}
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_safety("WHL_SPD11", 0, values, fix_checksum=checksum)
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_safety("SCC12", self.SCC_BUS, values, fix_checksum=checksum)
def _torque_driver_msg(self, torque):
values = {"CR_Mdps_StrColTq": torque}
return self.packer.make_can_msg_safety("MDPS12", 0, 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_safety("LKAS11", 0, values)
def _acc_state_msg(self, enable):
values = {"MainMode_ACC": enable}
return self.packer.make_can_msg_safety("SCC11", self.SCC_BUS, values)
def _lkas_button_msg(self, enabled):
if self.SAFETY_PARAM_IQ & HyundaiSafetyFlagsIQ.HAS_LDA_BUTTON:
values = {"LDA_BTN": enabled}
return self.packer.make_can_msg_safety("BCM_PO_11", 0, values)
else:
raise NotImplementedError
def _main_cruise_button_msg(self, enabled):
return self._button_msg(0, enabled)
def test_pcm_main_cruise_state_availability(self):
"""Test that ACC main state is correctly set when receiving 0x420 message, toggling HYUNDAI_LONG flag"""
prior_safety_mode = self.safety.get_current_safety_mode()
prior_safety_param = self.safety.get_current_safety_param()
for hyundai_longitudinal in (True, False):
with self.subTest("hyundai_longitudinal", hyundai_longitudinal=hyundai_longitudinal):
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundai, 0 if hyundai_longitudinal else HyundaiSafetyFlags.LONG)
for should_turn_acc_main_on in (True, False):
with self.subTest("acc_main_on", should_turn_acc_main_on=should_turn_acc_main_on):
self._rx(self._acc_state_msg(should_turn_acc_main_on)) # Send the ACC state message
expected_acc_main = should_turn_acc_main_on and hyundai_longitudinal # ACC main should only be set if hyundai_longitudinal is True
self.assertEqual(expected_acc_main, self.safety.get_acc_main_on())
self.safety.set_safety_hooks(prior_safety_mode, prior_safety_param)
def test_enable_control_allowed_with_aol_button(self):
default_safety_mode = self.safety.get_current_safety_mode()
default_safety_param = self.safety.get_current_safety_param()
default_safety_param_iq = self.safety.get_current_safety_param_iq()
"""Toggle AOL with AOL button"""
try:
self._lkas_button_msg(False)
except NotImplementedError as err:
raise unittest.SkipTest("Skipping test because LDA button is not supported") from err
try:
for enable_aol in (True, False):
with self.subTest("enable_aol", aol_enabled=enable_aol):
for has_lda_button_param in (True, False):
with self.subTest("has_lda_button", has_lda_button_param=has_lda_button_param):
has_lda_button = HyundaiSafetyFlagsIQ.HAS_LDA_BUTTON if has_lda_button_param else 0
self.safety.set_current_safety_param_iq(has_lda_button)
self.safety.set_safety_hooks(default_safety_mode, default_safety_param)
self.safety.set_aol_params(enable_aol, False, False)
self.assertEqual(enable_aol, self.safety.get_enable_aol())
self._rx(self._lkas_button_msg(True))
self._rx(self._speed_msg(0))
self._rx(self._lkas_button_msg(False))
self._rx(self._speed_msg(0))
self.assertEqual(enable_aol and has_lda_button_param, self.safety.get_controls_allowed_lat())
finally:
self.safety.set_current_safety_param_iq(default_safety_param_iq)
@parameterized_class(LDA_BUTTON)
class TestHyundaiSafetyAltLimits(TestHyundaiSafety):
MAX_RATE_UP = 2
MAX_RATE_DOWN = 3
MAX_TORQUE_LOOKUP = [0], [270]
@classmethod
def setUpClass(cls):
if cls.__name__ == "TestHyundaiSafetyAltLimits":
cls.safety = None
raise unittest.SkipTest
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_current_safety_param_iq(self.SAFETY_PARAM_IQ)
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundai, HyundaiSafetyFlags.ALT_LIMITS)
self.safety.init_tests()
@parameterized_class(LDA_BUTTON)
class TestHyundaiSafetyAltLimits2(TestHyundaiSafety):
MAX_RATE_UP = 2
MAX_RATE_DOWN = 3
MAX_TORQUE_LOOKUP = [0], [170]
@classmethod
def setUpClass(cls):
if cls.__name__ == "TestHyundaiSafetyAltLimits2":
cls.safety = None
raise unittest.SkipTest
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_current_safety_param_iq(self.SAFETY_PARAM_IQ)
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundai, HyundaiSafetyFlags.ALT_LIMITS_2)
self.safety.init_tests()
@parameterized_class(LDA_BUTTON)
class TestHyundaiSafetyCameraSCC(TestHyundaiSafety):
BUTTONS_TX_BUS = 2 # tx on 2, rx on 0
SCC_BUS = 2 # rx on 2
@classmethod
def setUpClass(cls):
if cls.__name__ == "TestHyundaiSafetyCameraSCC":
cls.safety = None
raise unittest.SkipTest
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_current_safety_param_iq(self.SAFETY_PARAM_IQ)
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundai, HyundaiSafetyFlags.CAMERA_SCC)
self.safety.init_tests()
def test_pcm_main_cruise_state_availability(self):
"""
Test that ACC main state is correctly set when receiving 0x420 message.
For camera SCC, ACC main should always be on when receiving 0x420 message
"""
for should_turn_acc_main_on in (True, False):
with self.subTest("acc_main_on", should_turn_acc_main_on=should_turn_acc_main_on):
self._rx(self._acc_state_msg(should_turn_acc_main_on))
self.assertEqual(should_turn_acc_main_on, self.safety.get_acc_main_on())
@parameterized_class(LDA_BUTTON)
class TestHyundaiSafetyFCEV(TestHyundaiSafety):
@classmethod
def setUpClass(cls):
if cls.__name__ == "TestHyundaiSafetyFCEV":
cls.safety = None
raise unittest.SkipTest
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_current_safety_param_iq(self.SAFETY_PARAM_IQ)
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundai, HyundaiSafetyFlags.FCEV_GAS)
self.safety.init_tests()
def _user_gas_msg(self, gas):
values = {"ACCELERATOR_PEDAL": gas}
return self.packer.make_can_msg_safety("FCEV_ACCELERATOR", 0, values)
class TestHyundaiLegacySafety(TestHyundaiSafety):
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundaiLegacy, 0)
self.safety.init_tests()
class TestHyundaiLegacySafetyEV(TestHyundaiSafety):
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundaiLegacy, HyundaiSafetyFlags.EV_GAS)
self.safety.init_tests()
def _user_gas_msg(self, gas):
values = {"Accel_Pedal_Pos": gas}
return self.packer.make_can_msg_safety("E_EMS11", 0, values, fix_checksum=checksum)
class TestHyundaiLegacySafetyHEV(TestHyundaiSafety):
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundaiLegacy, HyundaiSafetyFlags.HYBRID_GAS)
self.safety.init_tests()
def _user_gas_msg(self, gas):
values = {"CR_Vcu_AccPedDep_Pos": gas}
return self.packer.make_can_msg_safety("E_EMS11", 0, values, fix_checksum=checksum)
@parameterized_class(LDA_BUTTON)
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]]
FWD_BLACKLISTED_ADDRS = {2: [0x340, 0x485, 0x421, 0x420, 0x50A, 0x389]}
RELAY_MALFUNCTION_ADDRS = {0: (0x340, 0x485, 0x421, 0x420, 0x50A, 0x389)} # LKAS11, LFAHDA_MFC, SCC12, SCC11, SCC13, SCC14
DISABLED_ECU_UDS_MSG = (0x7D0, 0)
DISABLED_ECU_ACTUATION_MSG = (0x421, 0)
@classmethod
def setUpClass(cls):
if cls.__name__ == "TestHyundaiLongitudinalSafety":
cls.safety = None
raise unittest.SkipTest
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_current_safety_param_iq(self.SAFETY_PARAM_IQ)
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundai, HyundaiSafetyFlags.LONG)
self.safety.init_tests()
def _accel_msg(self, accel, aeb_req=False, aeb_decel=0):
values = {
"aReqRaw": accel,
"aReqValue": accel,
"AEB_CmdAct": int(aeb_req),
"CR_VSM_DecCmd": aeb_decel,
}
return self.packer.make_can_msg_safety("SCC12", self.SCC_BUS, values)
def _fca11_msg(self, idx=0, vsm_aeb_req=False, fca_aeb_req=False, aeb_decel=0):
values = {
"CR_FCA_Alive": idx % 0xF,
"FCA_Status": 2,
"CR_VSM_DecCmd": aeb_decel,
"CF_VSM_DecCmdAct": int(vsm_aeb_req),
"FCA_CmdAct": int(fca_aeb_req),
}
return self.packer.make_can_msg_safety("FCA11", 0, values)
def _tx_acc_state_msg(self, enable):
values = {"MainMode_ACC": enable}
return self.packer.make_can_msg_safety("SCC11", 0, values)
def test_no_aeb_fca11(self):
self.assertTrue(self._tx(self._fca11_msg()))
self.assertFalse(self._tx(self._fca11_msg(vsm_aeb_req=True)))
self.assertFalse(self._tx(self._fca11_msg(fca_aeb_req=True)))
self.assertFalse(self._tx(self._fca11_msg(aeb_decel=1.0)))
def test_no_aeb_scc12(self):
self.assertTrue(self._tx(self._accel_msg(0)))
self.assertFalse(self._tx(self._accel_msg(0, aeb_req=True)))
self.assertFalse(self._tx(self._accel_msg(0, aeb_decel=1.0)))
class TestHyundaiLongitudinalSafetyCameraSCC(HyundaiLongitudinalBase, TestHyundaiSafety):
TX_MSGS = [[0x340, 0], [0x4F1, 2], [0x485, 0], [0x420, 0], [0x421, 0], [0x50A, 0], [0x389, 0], [0x4A2, 0]]
FWD_BLACKLISTED_ADDRS = {2: [0x340, 0x485, 0x420, 0x421, 0x50A, 0x389]}
RELAY_MALFUNCTION_ADDRS = {0: (0x340, 0x485, 0x421, 0x420, 0x50A, 0x389)} # LKAS11, LFAHDA_MFC, SCC12, SCC11, SCC13, SCC14
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_current_safety_param_iq(HyundaiSafetyFlagsIQ.HAS_LDA_BUTTON)
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundai, HyundaiSafetyFlags.LONG | HyundaiSafetyFlags.CAMERA_SCC)
self.safety.init_tests()
def _accel_msg(self, accel, aeb_req=False, aeb_decel=0):
values = {
"aReqRaw": accel,
"aReqValue": accel,
"AEB_CmdAct": int(aeb_req),
"CR_VSM_DecCmd": aeb_decel,
}
return self.packer.make_can_msg_safety("SCC12", self.SCC_BUS, values)
def _tx_acc_state_msg(self, enable):
values = {"MainMode_ACC": enable}
return self.packer.make_can_msg_safety("SCC11", self.SCC_BUS, values)
def test_no_aeb_scc12(self):
self.assertTrue(self._tx(self._accel_msg(0)))
self.assertFalse(self._tx(self._accel_msg(0, aeb_req=True)))
self.assertFalse(self._tx(self._accel_msg(0, aeb_decel=1.0)))
def test_tester_present_allowed(self):
pass
def test_disabled_ecu_alive(self):
pass
@parameterized_class(LDA_BUTTON)
class TestHyundaiSafetyFCEVLong(TestHyundaiLongitudinalSafety, TestHyundaiSafetyFCEV):
@classmethod
def setUpClass(cls):
if cls.__name__ == "TestHyundaiSafetyFCEVLong":
cls.safety = None
raise unittest.SkipTest
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_current_safety_param_iq(self.SAFETY_PARAM_IQ)
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundai, HyundaiSafetyFlags.FCEV_GAS | HyundaiSafetyFlags.LONG)
self.safety.init_tests()
@parameterized_class(LDA_BUTTON)
class TestHyundaiLongitudinalESCCSafety(HyundaiLongitudinalBase, TestHyundaiSafety):
TX_MSGS = [[0x340, 0], [0x4F1, 0], [0x485, 0], [0x420, 0], [0x421, 0], [0x50A, 0], [0x389, 0]]
FWD_BLACKLISTED_ADDRS = {2: [0x340, 0x485, 0x420, 0x421, 0x50A, 0x389]}
RELAY_MALFUNCTION_ADDRS = {0: (0x340, 0x485, 0x420, 0x421, 0x50A, 0x389)} # LKAS11, LFAHDA_MFC, SCC12, SCC11, SCC13, SCC14
@classmethod
def setUpClass(cls):
if cls.__name__ == "TestHyundaiLongitudinalESCCSafety":
cls.safety = None
raise unittest.SkipTest
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_current_safety_param_iq(HyundaiSafetyFlagsIQ.ESCC | self.SAFETY_PARAM_IQ)
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundai, HyundaiSafetyFlags.LONG)
self.safety.init_tests()
def _accel_msg(self, accel, aeb_req=False, aeb_decel=0):
values = {
"aReqRaw": accel,
"aReqValue": accel,
}
return self.packer.make_can_msg_safety("SCC12", self.SCC_BUS, values)
def _tx_acc_state_msg(self, enable):
values = {"MainMode_ACC": enable}
return self.packer.make_can_msg_safety("SCC11", 0, values)
def test_tester_present_allowed(self):
pass
def test_disabled_ecu_alive(self):
pass
@parameterized_class(LDA_BUTTON)
class TestHyundaiNonSCCSafety(TestHyundaiSafety):
@classmethod
def setUpClass(cls):
if cls.__name__ == "TestHyundaiNonSCCSafety":
cls.safety = None
raise unittest.SkipTest
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_current_safety_param_iq(HyundaiSafetyFlagsIQ.NON_SCC | self.SAFETY_PARAM_IQ)
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundai, 0)
self.safety.init_tests()
def _pcm_status_msg(self, enable):
values = {"CRUISE_LAMP_S": enable, "AliveCounter": self.cnt_gas % 4}
self.__class__.cnt_gas += 1
return self.packer.make_can_msg_safety("EMS16", 0, values, fix_checksum=checksum)
def _acc_state_msg(self, enable):
values = {"CRUISE_LAMP_M": enable, "AliveCounter": self.cnt_gas % 4}
self.__class__.cnt_gas += 1
return self.packer.make_can_msg_safety("EMS16", 0, values, fix_checksum=checksum)
def _user_gas_msg(self, gas: float, controls_allowed: bool = True):
values = {"CF_Ems_AclAct": gas, "CRUISE_LAMP_M": 1, "CRUISE_LAMP_S": controls_allowed, "AliveCounter": self.cnt_gas % 4}
self.__class__.cnt_gas += 1
return self.packer.make_can_msg_safety("EMS16", 0, values, fix_checksum=checksum)
def test_allow_engage_with_gas_pressed(self):
self._rx(self._user_gas_msg(1, self.safety.get_controls_allowed()))
self.safety.set_controls_allowed(True)
self._rx(self._user_gas_msg(1, self.safety.get_controls_allowed()))
self.assertTrue(self.safety.get_controls_allowed())
self._rx(self._user_gas_msg(1, self.safety.get_controls_allowed()))
self.assertTrue(self.safety.get_controls_allowed())
def test_no_disengage_on_gas(self):
self._rx(self._user_gas_msg(0, self.safety.get_controls_allowed()))
self.safety.set_controls_allowed(True)
self._rx(self._user_gas_msg(self.GAS_PRESSED_THRESHOLD + 1, self.safety.get_controls_allowed()))
# Test we allow lateral, but not longitudinal
self.assertTrue(self.safety.get_controls_allowed())
self.assertFalse(self.safety.get_longitudinal_brake_allowed())
self.assertTrue(self.safety.get_longitudinal_gas_allowed())
# Make sure we can re-gain longitudinal actuation
self._rx(self._user_gas_msg(0, self.safety.get_controls_allowed()))
self.assertTrue(self.safety.get_longitudinal_brake_allowed())
self.assertTrue(self.safety.get_longitudinal_gas_allowed())
@parameterized_class(ALL_NON_SCC_HEV_EV_COMBOS)
class TestHyundaiNonSCCSafety_HEV_EV(TestHyundaiSafety):
PCM_STATUS_MSG = ("", "")
ACC_STATE_MSG = ("", "")
GAS_MSG = ("", "")
SAFETY_PARAM = 0
@classmethod
def setUpClass(cls):
if cls.__name__ == "TestHyundaiNonSCCSafety_HEV_EV":
cls.safety = None
raise unittest.SkipTest
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_current_safety_param_iq(HyundaiSafetyFlagsIQ.NON_SCC | self.SAFETY_PARAM_IQ)
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundai, self.SAFETY_PARAM)
self.safety.init_tests()
def _pcm_status_msg(self, enable):
values = {self.PCM_STATUS_MSG[1]: enable}
return self.packer.make_can_msg_safety(self.PCM_STATUS_MSG[0], 0, values)
def _acc_state_msg(self, enable):
values = {self.ACC_STATE_MSG[1]: enable}
return self.packer.make_can_msg_safety(self.ACC_STATE_MSG[0], 0, values)
def _user_gas_msg(self, gas):
values = {self.GAS_MSG[1]: gas}
return self.packer.make_can_msg_safety(self.GAS_MSG[0], 0, values, fix_checksum=checksum)
if __name__ == "__main__":
unittest.main()
@pytest.fixture
def safety():
return libsafety_py.libsafety
@pytest.mark.parametrize("mode", (CarParams.SafetyModel.hyundai, CarParams.SafetyModel.hyundaiLegacy))
@pytest.mark.parametrize("param", (
0,
HyundaiSafetyFlags.EV_GAS,
HyundaiSafetyFlags.HYBRID_GAS,
HyundaiSafetyFlags.LONG,
HyundaiSafetyFlags.CAMERA_SCC,
HyundaiSafetyFlags.ALT_LIMITS,
HyundaiSafetyFlags.FCEV_GAS,
HyundaiSafetyFlags.ALT_LIMITS_2,
))
def test_classic_safety_configurations_initialize(safety, mode, param):
assert safety.set_safety_hooks(mode, param) == 0
safety.init_tests()
assert safety.get_current_safety_param() == param
@pytest.mark.parametrize("mode", (CarParams.SafetyModel.hyundai, CarParams.SafetyModel.hyundaiLegacy))
def test_classic_tx_whitelist_and_steering_limits(safety, mode):
safety.set_safety_hooks(mode, 0)
safety.init_tests()
assert not safety.safety_tx_hook(packet(0x123, 0, 8))
assert not safety.safety_tx_hook(packet(0x340, 1, 8))
safety.set_controls_allowed(False)
assert safety.safety_tx_hook(classic_steer(0, False))
assert not safety.safety_tx_hook(classic_steer(1))
safety.set_controls_allowed(True)
assert safety.safety_tx_hook(classic_steer(10))
safety.set_desired_torque_last(512)
safety.set_rt_torque_last(512)
assert safety.safety_tx_hook(classic_steer(512))
assert not safety.safety_tx_hook(classic_steer(513))
assert not safety.safety_tx_hook(classic_steer(-513))
def test_classic_alt_limits_2(safety):
safety.set_safety_hooks(CarParams.SafetyModel.hyundai, HyundaiSafetyFlags.ALT_LIMITS_2)
safety.init_tests()
safety.set_controls_allowed(True)
safety.set_desired_torque_last(170)
safety.set_rt_torque_last(170)
assert safety.safety_tx_hook(classic_steer(170))
assert not safety.safety_tx_hook(classic_steer(171))
def test_classic_longitudinal_accel_and_aeb_guards(safety):
safety.set_safety_hooks(CarParams.SafetyModel.hyundai, HyundaiSafetyFlags.LONG)
safety.init_tests()
safety.set_controls_allowed(False)
assert safety.safety_tx_hook(classic_accel(0))
assert not safety.safety_tx_hook(classic_accel(1))
safety.set_controls_allowed(True)
for accel in (-400, 0, 250):
assert safety.safety_tx_hook(classic_accel(accel))
for accel in (-401, 251):
assert not safety.safety_tx_hook(classic_accel(accel))
assert not safety.safety_tx_hook(classic_accel(0, aeb_decel=1))
assert not safety.safety_tx_hook(classic_accel(0, aeb_request=True))
assert safety.safety_tx_hook(packet(0x38D, 0, 8))
assert not safety.safety_tx_hook(packet(0x38D, 0, 8, {1: 1}))
assert not safety.safety_tx_hook(packet(0x38D, 0, 8, {2: 1 << 4}))
assert not safety.safety_tx_hook(packet(0x38D, 0, 8, {3: 1 << 7}))
def test_classic_buttons(safety):
safety.set_safety_hooks(CarParams.SafetyModel.hyundai, 0)
safety.init_tests()
safety.set_controls_allowed(False)
assert not safety.safety_tx_hook(packet(0x4F1, 0, 4, {0: 1}))
assert not safety.safety_tx_hook(packet(0x4F1, 0, 4, {0: 2}))
assert not safety.safety_tx_hook(packet(0x4F1, 0, 4, {0: 4}))
safety.set_controls_allowed(True)
assert safety.safety_tx_hook(packet(0x4F1, 0, 4, {0: 1}))
assert not safety.safety_tx_hook(packet(0x4F1, 0, 4, {0: 2}))
assert safety.safety_tx_hook(packet(0x4F1, 0, 4, {0: 4}))
safety.set_controls_allowed(False)
safety.set_cruise_engaged_prev(True)
assert safety.safety_tx_hook(packet(0x4F1, 0, 4, {0: 4}))
def test_classic_diagnostic_payload_is_restricted(safety):
safety.set_safety_hooks(CarParams.SafetyModel.hyundai, HyundaiSafetyFlags.LONG)
safety.init_tests()
assert safety.safety_tx_hook(packet(0x7D0, 0, 8, dict(enumerate(TESTER_PRESENT))))
assert not safety.safety_tx_hook(packet(0x7D0, 0, 8, {0: 3, 1: 0x22}))

404
iqdbc_repo/iqdbc/safety/tests/test_hyundai_canfd.py Executable file → Normal file
View File

@@ -1,312 +1,104 @@
#!/usr/bin/env python3
from parameterized import parameterized_class
import unittest
import pytest
from iqdbc.car.hyundai.values import HyundaiSafetyFlags
from iqdbc.car.structs import CarParams
from iqdbc.safety.tests.hyundai_common import TESTER_PRESENT, canfd_accel, canfd_steer, packet
from iqdbc.safety.tests.libsafety import libsafety_py
import iqdbc.safety.tests.common as common
from iqdbc.safety.tests.common import CANPackerSafety
from iqdbc.safety.tests.hyundai_common import HyundaiButtonBase, HyundaiLongitudinalBase
# All combinations of radar/camera-SCC and gas/hybrid/EV cars
ALL_GAS_EV_HYBRID_COMBOS = [
# Radar SCC
{"GAS_MSG": ("ACCELERATOR_BRAKE_ALT", "ACCELERATOR_PEDAL_PRESSED"), "SCC_BUS": 0, "SAFETY_PARAM": 0},
{"GAS_MSG": ("ACCELERATOR", "ACCELERATOR_PEDAL"), "SCC_BUS": 0, "SAFETY_PARAM": HyundaiSafetyFlags.EV_GAS},
{"GAS_MSG": ("ACCELERATOR_ALT", "ACCELERATOR_PEDAL"), "SCC_BUS": 0, "SAFETY_PARAM": HyundaiSafetyFlags.HYBRID_GAS},
# Camera SCC
{"GAS_MSG": ("ACCELERATOR_BRAKE_ALT", "ACCELERATOR_PEDAL_PRESSED"), "SCC_BUS": 2, "SAFETY_PARAM": HyundaiSafetyFlags.CAMERA_SCC},
{"GAS_MSG": ("ACCELERATOR", "ACCELERATOR_PEDAL"), "SCC_BUS": 2, "SAFETY_PARAM": HyundaiSafetyFlags.EV_GAS | HyundaiSafetyFlags.CAMERA_SCC},
{"GAS_MSG": ("ACCELERATOR_ALT", "ACCELERATOR_PEDAL"), "SCC_BUS": 2, "SAFETY_PARAM": HyundaiSafetyFlags.HYBRID_GAS | HyundaiSafetyFlags.CAMERA_SCC},
]
class TestHyundaiCanfdBase(HyundaiButtonBase, common.CarSafetyTest, common.DriverTorqueSteeringSafetyTest, common.SteerRequestCutSafetyTest):
TX_MSGS = [[0x50, 0], [0x1CF, 1], [0x2A4, 0]]
STANDSTILL_THRESHOLD = 12 # 0.375 kph
FWD_BLACKLISTED_ADDRS = {2: [0x50, 0x2a4]}
MAX_RATE_UP = 2
MAX_RATE_DOWN = 3
MAX_TORQUE_LOOKUP = [0], [270]
MAX_RT_DELTA = 112
DRIVER_TORQUE_ALLOWANCE = 250
DRIVER_TORQUE_FACTOR = 2
# Safety around steering req bit
MIN_VALID_STEERING_FRAMES = 89
MAX_INVALID_STEERING_FRAMES = 2
PT_BUS = 0
SCC_BUS = 2
STEER_BUS = 0
STEER_MSG = ""
GAS_MSG = ("", "")
BUTTONS_TX_BUS = 1
def _torque_driver_msg(self, torque):
values = {"STEERING_COL_TORQUE": torque}
return self.packer.make_can_msg_safety("MDPS", self.PT_BUS, values)
def _torque_cmd_msg(self, torque, steer_req=1):
values = {"TORQUE_REQUEST": torque, "STEER_REQ": steer_req}
return self.packer.make_can_msg_safety(self.STEER_MSG, self.STEER_BUS, values)
def _speed_msg(self, speed):
values = {f"WHL_Spd{pos}Val": speed * 0.03125 for pos in ["FL", "FR", "RL", "RR"]}
return self.packer.make_can_msg_safety("WHEEL_SPEEDS", self.PT_BUS, values)
def _user_brake_msg(self, brake):
values = {"DriverBraking": brake}
return self.packer.make_can_msg_safety("TCS", self.PT_BUS, values)
def _user_gas_msg(self, gas):
values = {self.GAS_MSG[1]: gas}
return self.packer.make_can_msg_safety(self.GAS_MSG[0], self.PT_BUS, values)
def _pcm_status_msg(self, enable):
values = {"ACCMode": 1 if enable else 0}
return self.packer.make_can_msg_safety("SCC_CONTROL", self.SCC_BUS, values)
def _button_msg(self, buttons, main_button=0, bus=None):
if bus is None:
bus = self.PT_BUS
values = {
"CRUISE_BUTTONS": buttons,
"ADAPTIVE_CRUISE_MAIN_BTN": main_button,
}
return self.packer.make_can_msg_safety("CRUISE_BUTTONS", bus, values)
def _acc_state_msg(self, enable):
values = {"MainMode_ACC": enable}
return self.packer.make_can_msg_safety("SCC_CONTROL", self.SCC_BUS, values)
def _lkas_button_msg(self, enabled):
values = {"LDA_BTN": enabled}
return self.packer.make_can_msg_safety("CRUISE_BUTTONS", self.PT_BUS, values)
def _main_cruise_button_msg(self, enabled):
return self._button_msg(0, enabled)
class TestHyundaiCanfdLFASteeringBase(TestHyundaiCanfdBase):
TX_MSGS = [[0x12A, 0], [0x1A0, 1], [0x1CF, 0], [0x1E0, 0]]
RELAY_MALFUNCTION_ADDRS = {0: (0x12A, 0x1E0)} # LFA, LFAHDA_CLUSTER
FWD_BLACKLISTED_ADDRS = {2: [0x12A, 0x1E0]}
STEER_MSG = "LFA"
BUTTONS_TX_BUS = 2
SAFETY_PARAM: int
@classmethod
def setUpClass(cls):
super().setUpClass()
if cls.__name__ in ("TestHyundaiCanfdLFASteering", "TestHyundaiCanfdLFASteeringAltButtons"):
cls.packer = None
cls.safety = None
raise unittest.SkipTest
def setUp(self):
self.packer = CANPackerSafety("hyundai_canfd_generated")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundaiCanfd, self.SAFETY_PARAM)
self.safety.init_tests()
@parameterized_class(ALL_GAS_EV_HYBRID_COMBOS)
class TestHyundaiCanfdLFASteering(TestHyundaiCanfdLFASteeringBase):
pass
class TestHyundaiCanfdLFASteeringAltButtonsBase(TestHyundaiCanfdLFASteeringBase):
SAFETY_PARAM: int
def setUp(self):
self.packer = CANPackerSafety("hyundai_canfd_generated")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundaiCanfd, HyundaiSafetyFlags.CANFD_ALT_BUTTONS | self.SAFETY_PARAM)
self.safety.init_tests()
def _button_msg(self, buttons, main_button=0, bus=1):
values = {
"CRUISE_BUTTONS": buttons,
"ADAPTIVE_CRUISE_MAIN_BTN": main_button,
}
return self.packer.make_can_msg_safety("CRUISE_BUTTONS_ALT", self.PT_BUS, values)
def _lkas_button_msg(self, enabled):
values = {"LDA_BTN": enabled}
return self.packer.make_can_msg_safety("CRUISE_BUTTONS_ALT", self.PT_BUS, values)
def _acc_cancel_msg(self, cancel, accel=0):
values = {"ACCMode": 4 if cancel else 0, "aReqRaw": accel, "aReqValue": accel}
return self.packer.make_can_msg_safety("SCC_CONTROL", self.PT_BUS, values)
def test_button_sends(self):
"""
No button send allowed with alt buttons.
"""
for enabled in (True, False):
for btn in range(8):
self.safety.set_controls_allowed(enabled)
self.assertFalse(self._tx(self._button_msg(btn)))
def test_acc_cancel(self):
# FIXME: the CANFD_ALT_BUTTONS cars are the only ones that use SCC_CONTROL to cancel, why can't we use buttons?
for enabled in (True, False):
self.safety.set_controls_allowed(enabled)
self.assertTrue(self._tx(self._acc_cancel_msg(True)))
self.assertFalse(self._tx(self._acc_cancel_msg(True, accel=1)))
self.assertFalse(self._tx(self._acc_cancel_msg(False)))
@parameterized_class(ALL_GAS_EV_HYBRID_COMBOS)
class TestHyundaiCanfdLFASteeringAltButtons(TestHyundaiCanfdLFASteeringAltButtonsBase):
pass
class TestHyundaiCanfdLKASteeringEV(TestHyundaiCanfdBase):
TX_MSGS = [[0x50, 0], [0x1CF, 1], [0x2A4, 0]]
RELAY_MALFUNCTION_ADDRS = {0: (0x50, 0x2a4)} # LKAS, CAM_0x2A4
FWD_BLACKLISTED_ADDRS = {2: [0x50, 0x2a4]}
PT_BUS = 1
SCC_BUS = 1
STEER_MSG = "LKAS"
GAS_MSG = ("ACCELERATOR", "ACCELERATOR_PEDAL")
def setUp(self):
self.packer = CANPackerSafety("hyundai_canfd_generated")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundaiCanfd, HyundaiSafetyFlags.CANFD_LKA_STEERING | HyundaiSafetyFlags.EV_GAS)
self.safety.init_tests()
# TODO: Handle ICE and HEV configurations once we see cars that use the new messages
class TestHyundaiCanfdLKASteeringAltEV(TestHyundaiCanfdBase):
TX_MSGS = [[0x110, 0], [0x1CF, 1], [0x362, 0]]
RELAY_MALFUNCTION_ADDRS = {0: (0x110, 0x362)} # LKAS_ALT, CAM_0x362
FWD_BLACKLISTED_ADDRS = {2: [0x110, 0x362]}
PT_BUS = 1
SCC_BUS = 1
STEER_MSG = "LKAS_ALT"
GAS_MSG = ("ACCELERATOR", "ACCELERATOR_PEDAL")
def setUp(self):
self.packer = CANPackerSafety("hyundai_canfd_generated")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundaiCanfd, HyundaiSafetyFlags.CANFD_LKA_STEERING | HyundaiSafetyFlags.EV_GAS |
HyundaiSafetyFlags.CANFD_LKA_STEERING_ALT)
self.safety.init_tests()
class TestHyundaiCanfdLKASteeringLongEV(HyundaiLongitudinalBase, TestHyundaiCanfdLKASteeringEV):
TX_MSGS = [[0x50, 0], [0x1CF, 1], [0x2A4, 0], [0x51, 0], [0x730, 1], [0x12a, 1], [0x160, 1],
[0x1e0, 1], [0x1a0, 1], [0x1ea, 1], [0x200, 1], [0x345, 1], [0x1da, 1]]
RELAY_MALFUNCTION_ADDRS = {0: (0x50, 0x2a4), 1: (0x1a0,)} # LKAS, CAM_0x2A4, SCC_CONTROL
DISABLED_ECU_UDS_MSG = (0x730, 1)
DISABLED_ECU_ACTUATION_MSG = (0x1a0, 1)
STEER_MSG = "LFA"
GAS_MSG = ("ACCELERATOR", "ACCELERATOR_PEDAL")
STEER_BUS = 1
def setUp(self):
self.packer = CANPackerSafety("hyundai_canfd_generated")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundaiCanfd, HyundaiSafetyFlags.CANFD_LKA_STEERING |
HyundaiSafetyFlags.LONG | HyundaiSafetyFlags.EV_GAS)
self.safety.init_tests()
def _accel_msg(self, accel, aeb_req=False, aeb_decel=0):
values = {
"aReqRaw": accel,
"aReqValue": accel,
}
return self.packer.make_can_msg_safety("SCC_CONTROL", self.PT_BUS, values)
def _tx_acc_state_msg(self, enable):
values = {"MainMode_ACC": enable}
return self.packer.make_can_msg_safety("SCC_CONTROL", self.PT_BUS, values)
# Tests longitudinal for ICE, hybrid, EV cars with LFA steering
class TestHyundaiCanfdLFASteeringLongBase(HyundaiLongitudinalBase, TestHyundaiCanfdLFASteeringBase):
FWD_BLACKLISTED_ADDRS = {2: [0x12a, 0x1e0, 0x1a0, 0x160]}
RELAY_MALFUNCTION_ADDRS = {0: (0x12A, 0x1E0, 0x1a0, 0x160)} # LFA, LFAHDA_CLUSTER, SCC_CONTROL, ADRV_0x160
DISABLED_ECU_UDS_MSG = (0x7D0, 0)
DISABLED_ECU_ACTUATION_MSG = (0x1a0, 0)
@classmethod
def setUpClass(cls):
if cls.__name__ == "TestHyundaiCanfdLFASteeringLongBase":
cls.safety = None
raise unittest.SkipTest
def setUp(self):
self.packer = CANPackerSafety("hyundai_canfd_generated")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundaiCanfd, HyundaiSafetyFlags.LONG | self.SAFETY_PARAM)
self.safety.init_tests()
def _accel_msg(self, accel, aeb_req=False, aeb_decel=0):
values = {
"aReqRaw": accel,
"aReqValue": accel,
}
return self.packer.make_can_msg_safety("SCC_CONTROL", self.PT_BUS, values)
def _tx_acc_state_msg(self, enable):
values = {"MainMode_ACC": enable}
return self.packer.make_can_msg_safety("SCC_CONTROL", self.PT_BUS, values)
# no knockout
def test_tester_present_allowed(self):
pass
@parameterized_class(ALL_GAS_EV_HYBRID_COMBOS)
class TestHyundaiCanfdLFASteeringLong(TestHyundaiCanfdLFASteeringLongBase):
@classmethod
def setUpClass(cls):
if cls.__name__ == "TestHyundaiCanfdLFASteeringLong":
cls.safety = None
raise unittest.SkipTest
@parameterized_class(ALL_GAS_EV_HYBRID_COMBOS)
class TestHyundaiCanfdLFASteeringLongAltButtons(TestHyundaiCanfdLFASteeringLongBase, TestHyundaiCanfdLFASteeringAltButtonsBase):
@classmethod
def setUpClass(cls):
if cls.__name__ == "TestHyundaiCanfdLFASteeringLongAltButtons":
cls.safety = None
raise unittest.SkipTest
def setUp(self):
self.packer = CANPackerSafety("hyundai_canfd_generated")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundaiCanfd, HyundaiSafetyFlags.LONG | HyundaiSafetyFlags.CANFD_ALT_BUTTONS | self.SAFETY_PARAM)
self.safety.init_tests()
def test_acc_cancel(self):
# Alt buttons does not use SCC_CONTROL to cancel if longitudinal
pass
if __name__ == "__main__":
unittest.main()
@pytest.fixture
def safety():
return libsafety_py.libsafety
@pytest.mark.parametrize("param", (
0,
HyundaiSafetyFlags.EV_GAS,
HyundaiSafetyFlags.HYBRID_GAS,
HyundaiSafetyFlags.LONG,
HyundaiSafetyFlags.CAMERA_SCC,
HyundaiSafetyFlags.CANFD_LKA_STEERING,
HyundaiSafetyFlags.CANFD_ALT_BUTTONS,
HyundaiSafetyFlags.CANFD_LKA_STEERING | HyundaiSafetyFlags.CANFD_LKA_STEERING_ALT,
HyundaiSafetyFlags.CANFD_LKA_STEERING | HyundaiSafetyFlags.LONG,
HyundaiSafetyFlags.CANFD_LKA_STEERING | HyundaiSafetyFlags.LONG | HyundaiSafetyFlags.CANFD_ALT_BUTTONS,
))
def test_canfd_safety_configurations_initialize(safety, param):
assert safety.set_safety_hooks(CarParams.SafetyModel.hyundaiCanfd, param) == 0
safety.init_tests()
assert safety.get_current_safety_param() == param
@pytest.mark.parametrize(("param", "addr", "length"), (
(0, 0x12A, 16),
(HyundaiSafetyFlags.CANFD_LKA_STEERING, 0x50, 16),
(HyundaiSafetyFlags.CANFD_LKA_STEERING | HyundaiSafetyFlags.CANFD_LKA_STEERING_ALT, 0x110, 32),
(HyundaiSafetyFlags.CANFD_LKA_STEERING | HyundaiSafetyFlags.LONG, 0x12A, 16),
))
def test_canfd_steering_limits(safety, param, addr, length):
safety.set_safety_hooks(CarParams.SafetyModel.hyundaiCanfd, param)
safety.init_tests()
safety.set_controls_allowed(False)
assert safety.safety_tx_hook(canfd_steer(addr, length, 0, False))
assert not safety.safety_tx_hook(canfd_steer(addr, length, 1))
safety.set_controls_allowed(True)
assert safety.safety_tx_hook(canfd_steer(addr, length, 10))
safety.set_desired_torque_last(512)
safety.set_rt_torque_last(512)
assert safety.safety_tx_hook(canfd_steer(addr, length, 512))
assert not safety.safety_tx_hook(canfd_steer(addr, length, 513))
assert not safety.safety_tx_hook(canfd_steer(addr, length, -513))
def test_canfd_tx_whitelist_and_buttons(safety):
safety.set_safety_hooks(CarParams.SafetyModel.hyundaiCanfd, 0)
safety.init_tests()
assert not safety.safety_tx_hook(packet(0x123, 0, 8))
assert not safety.safety_tx_hook(packet(0x12A, 1, 16))
safety.set_controls_allowed(False)
assert not safety.safety_tx_hook(packet(0x1CF, 0, 8, {2: 1}))
assert not safety.safety_tx_hook(packet(0x1CF, 0, 8, {2: 2}))
assert not safety.safety_tx_hook(packet(0x1CF, 0, 8, {2: 4}))
safety.set_controls_allowed(True)
assert safety.safety_tx_hook(packet(0x1CF, 0, 8, {2: 1}))
assert not safety.safety_tx_hook(packet(0x1CF, 0, 8, {2: 2}))
assert safety.safety_tx_hook(packet(0x1CF, 0, 8, {2: 4}))
safety.set_controls_allowed(False)
safety.set_cruise_engaged_prev(True)
assert safety.safety_tx_hook(packet(0x1CF, 0, 8, {2: 4}))
def test_canfd_stock_longitudinal_only_allows_cancel(safety):
safety.set_safety_hooks(CarParams.SafetyModel.hyundaiCanfd, 0)
safety.init_tests()
assert safety.safety_tx_hook(canfd_accel(0, acc_mode=4))
assert not safety.safety_tx_hook(canfd_accel(1, acc_mode=4))
assert not safety.safety_tx_hook(canfd_accel(0, acc_mode=0))
def test_canfd_longitudinal_accel_limits(safety):
safety.set_safety_hooks(CarParams.SafetyModel.hyundaiCanfd, HyundaiSafetyFlags.LONG)
safety.init_tests()
safety.set_controls_allowed(False)
assert safety.safety_tx_hook(canfd_accel(0))
assert not safety.safety_tx_hook(canfd_accel(1))
safety.set_controls_allowed(True)
for accel in (-400, 0, 250):
assert safety.safety_tx_hook(canfd_accel(accel))
for accel in (-401, 251):
assert not safety.safety_tx_hook(canfd_accel(accel))
def test_canfd_hda2_diagnostic_payload_is_restricted(safety):
param = HyundaiSafetyFlags.CANFD_LKA_STEERING | HyundaiSafetyFlags.LONG
safety.set_safety_hooks(CarParams.SafetyModel.hyundaiCanfd, param)
safety.init_tests()
assert safety.safety_tx_hook(packet(0x730, 1, 8, dict(enumerate(TESTER_PRESENT))))
assert not safety.safety_tx_hook(packet(0x730, 1, 8, {0: 3, 1: 0x22}))

View File

@@ -0,0 +1,39 @@
import pytest
from iqdbc.car import gen_empty_fingerprint, structs
from iqdbc.car.hyundai.interface import CarInterface
from iqdbc.car.hyundai.values import CAR
from iqdbc.safety.tests.libsafety import libsafety_py
@pytest.mark.parametrize("candidate", list(CAR), ids=lambda candidate: candidate.value)
@pytest.mark.parametrize("alpha_long", (False, True), ids=("stock_long", "openpilot_long"))
def test_controller_frames_match_configured_safety(candidate, alpha_long, monkeypatch, tmp_path):
"""Run real controller output through the safety configuration selected for every HKG platform."""
monkeypatch.setenv("PARAMS_ROOT", str(tmp_path / candidate.value))
fingerprint = gen_empty_fingerprint()
cp = CarInterface.get_params(candidate, fingerprint, [], alpha_long, False, False)
cp_iq = CarInterface.get_params_iq(cp, candidate, fingerprint, [], alpha_long, False, False)
interface = CarInterface(cp, cp_iq)
interface.update([])
safety_config = cp.safetyConfigs[-1]
safety = libsafety_py.libsafety
assert safety.set_safety_hooks(safety_config.safetyModel.raw, safety_config.safetyParam) == 0
safety.init_tests()
safety.set_controls_allowed(True)
control = structs.CarControl.new_message()
control.enabled = True
control.latActive = True
control.longActive = alpha_long
control.actuators.torque = 0.01
control.actuators.accel = 0.0
for frame in range(20):
_, can_sends = interface.apply(control.as_reader(), structs.IQCarControl())
assert isinstance(can_sends, list)
for address, data, bus in can_sends:
packet = libsafety_py.make_CANPacket(address, bus, data)
rejection = f"{candidate.value} frame {frame}: safety rejected address={address:#x} bus={bus} data={data.hex()}"
assert safety.safety_tx_hook(packet), rejection