const int HYUNDAI_MAX_STEER = 384; // like stock const int HYUNDAI_MAX_RT_DELTA = 112; // max delta torque allowed for real time checks const uint32_t HYUNDAI_RT_INTERVAL = 250000; // 250ms between real time checks const int HYUNDAI_MAX_RATE_UP = 3; const int HYUNDAI_MAX_RATE_DOWN = 7; const int HYUNDAI_DRIVER_TORQUE_ALLOWANCE = 50; const int HYUNDAI_DRIVER_TORQUE_FACTOR = 2; const int HYUNDAI_STANDSTILL_THRSLD = 30; // ~1kph const CanMsg HYUNDAI_TX_MSGS[] = { {832, 0, 8}, // LKAS11 Bus 0 {1265, 0, 4}, // CLU11 Bus 0 {1157, 0, 4}, // LFAHDA_MFC Bus 0 // {1056, 0, 8}, // SCC11, Bus 0 // {1057, 0, 8}, // SCC12, Bus 0 // {1290, 0, 8}, // SCC13, Bus 0 // {905, 0, 8}, // SCC14, Bus 0 // {1186, 0, 8} // 4a2SCC, Bus 0 }; AddrCheckStruct hyundai_rx_checks[] = { {.msg = {{608, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}}}, {.msg = {{902, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 10000U}}}, {.msg = {{916, 0, 8, .check_checksum = true, .max_counter = 7U, .expected_timestep = 10000U}}}, {.msg = {{1057, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}}}, }; const int HYUNDAI_RX_CHECK_LEN = sizeof(hyundai_rx_checks) / sizeof(hyundai_rx_checks[0]); // older hyundai models have less checks due to missing counters and checksums AddrCheckStruct hyundai_legacy_rx_checks[] = { {.msg = {{608, 0, 8, .check_checksum = true, .max_counter = 3U, .expected_timestep = 10000U}, {881, 0, 8, .expected_timestep = 10000U}}}, {.msg = {{902, 0, 8, .expected_timestep = 10000U}}}, {.msg = {{916, 0, 8, .expected_timestep = 10000U}}}, {.msg = {{1057, 0, 8, .check_checksum = true, .max_counter = 15U, .expected_timestep = 20000U}}}, }; const int HYUNDAI_LEGACY_RX_CHECK_LEN = sizeof(hyundai_legacy_rx_checks) / sizeof(hyundai_legacy_rx_checks[0]); bool hyundai_legacy = false; static uint8_t hyundai_get_counter(CAN_FIFOMailBox_TypeDef *to_push) { int addr = GET_ADDR(to_push); uint8_t cnt; if (addr == 608) { cnt = (GET_BYTE(to_push, 7) >> 4) & 0x3; } else if (addr == 902) { cnt = ((GET_BYTE(to_push, 3) >> 6) << 2) | (GET_BYTE(to_push, 1) >> 6); } else if (addr == 916) { cnt = (GET_BYTE(to_push, 1) >> 5) & 0x7; } else if (addr == 1057) { cnt = GET_BYTE(to_push, 7) & 0xF; } else { cnt = 0; } return cnt; } static uint8_t hyundai_get_checksum(CAN_FIFOMailBox_TypeDef *to_push) { int addr = GET_ADDR(to_push); uint8_t chksum; if (addr == 608) { chksum = GET_BYTE(to_push, 7) & 0xF; } else if (addr == 902) { chksum = ((GET_BYTE(to_push, 7) >> 6) << 2) | (GET_BYTE(to_push, 5) >> 6); } else if (addr == 916) { chksum = GET_BYTE(to_push, 6) & 0xF; } else if (addr == 1057) { chksum = GET_BYTE(to_push, 7) >> 4; } else { chksum = 0; } return chksum; } static uint8_t hyundai_compute_checksum(CAN_FIFOMailBox_TypeDef *to_push) { int addr = GET_ADDR(to_push); uint8_t chksum = 0; if (addr == 902) { // count the bits for (int i = 0; i < 8; i++) { uint8_t b = GET_BYTE(to_push, i); for (int j = 0; j < 8; j++) { uint8_t bit = 0; // exclude checksum and counter if (((i != 1) || (j < 6)) && ((i != 3) || (j < 6)) && ((i != 5) || (j < 6)) && ((i != 7) || (j < 6))) { bit = (b >> (uint8_t)j) & 1U; } chksum += bit; } } chksum = (chksum ^ 9U) & 15U; } else { // sum of nibbles for (int i = 0; i < 8; i++) { if ((addr == 916) && (i == 7)) { continue; // exclude } uint8_t b = GET_BYTE(to_push, i); if (((addr == 608) && (i == 7)) || ((addr == 916) && (i == 6)) || ((addr == 1057) && (i == 7))) { b &= (addr == 1057) ? 0x0FU : 0xF0U; // remove checksum } chksum += (b % 16U) + (b / 16U); } chksum = (16U - (chksum % 16U)) % 16U; } return chksum; } static int hyundai_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { bool valid; if (hyundai_legacy) { valid = addr_safety_check(to_push, hyundai_legacy_rx_checks, HYUNDAI_LEGACY_RX_CHECK_LEN, hyundai_get_checksum, hyundai_compute_checksum, hyundai_get_counter); } else { valid = addr_safety_check(to_push, hyundai_rx_checks, HYUNDAI_RX_CHECK_LEN, hyundai_get_checksum, hyundai_compute_checksum, hyundai_get_counter); } if (valid && (GET_BUS(to_push) == 0)) { int addr = GET_ADDR(to_push); if (addr == 593) { int torque_driver_new = ((GET_BYTES_04(to_push) & 0x7ff) * 0.79) - 808; // scale down new driver torque signal to match previous one // update array of samples update_sample(&torque_driver, torque_driver_new); } // enter controls on rising edge of ACC, exit controls on ACC off if (addr == 1057) { // 2 bits: 13-14 int cruise_engaged = (GET_BYTES_04(to_push) >> 13) & 0x3; if (cruise_engaged && !cruise_engaged_prev) { controls_allowed = 1; } if (!cruise_engaged) { controls_allowed = 0; } cruise_engaged_prev = cruise_engaged; } if ((addr == 608) || (hyundai_legacy && (addr == 881))) { if (addr == 608) { gas_pressed = (GET_BYTE(to_push, 7) >> 6) != 0; } else { gas_pressed = (((GET_BYTE(to_push, 4) & 0x7F) << 1) | GET_BYTE(to_push, 3) >> 7) != 0; } } // sample wheel speed, averaging opposite corners if (addr == 902) { int hyundai_speed = GET_BYTES_04(to_push) & 0x3FFF; // FL hyundai_speed += (GET_BYTES_48(to_push) >> 16) & 0x3FFF; // RL hyundai_speed /= 2; vehicle_moving = hyundai_speed > HYUNDAI_STANDSTILL_THRSLD; } if (addr == 916) { brake_pressed = (GET_BYTE(to_push, 6) >> 7) != 0; } generic_rx_checks((addr == 832)); } return valid; } static int hyundai_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { int tx = 1; int addr = GET_ADDR(to_send); if (!msg_allowed(to_send, HYUNDAI_TX_MSGS, sizeof(HYUNDAI_TX_MSGS)/sizeof(HYUNDAI_TX_MSGS[0]))) { tx = 0; } if (relay_malfunction) { tx = 0; } // LKA STEER: safety check if (addr == 832) { int desired_torque = ((GET_BYTES_04(to_send) >> 16) & 0x7ff) - 1024; uint32_t ts = TIM2->CNT; bool violation = 0; if (controls_allowed) { // *** global torque limit check *** violation |= max_limit_check(desired_torque, HYUNDAI_MAX_STEER, -HYUNDAI_MAX_STEER); // *** torque rate limit check *** violation |= driver_limit_check(desired_torque, desired_torque_last, &torque_driver, HYUNDAI_MAX_STEER, HYUNDAI_MAX_RATE_UP, HYUNDAI_MAX_RATE_DOWN, HYUNDAI_DRIVER_TORQUE_ALLOWANCE, HYUNDAI_DRIVER_TORQUE_FACTOR); // used next time desired_torque_last = desired_torque; // *** torque real time rate limit check *** violation |= rt_rate_limit_check(desired_torque, rt_torque_last, HYUNDAI_MAX_RT_DELTA); // every RT_INTERVAL set the new limits uint32_t ts_elapsed = get_ts_elapsed(ts, ts_last); if (ts_elapsed > HYUNDAI_RT_INTERVAL) { rt_torque_last = desired_torque; ts_last = ts; } } // no torque if controls is not allowed if (!controls_allowed && (desired_torque != 0)) { violation = 1; } // reset to 0 if either controls is not allowed or there's a violation if (violation || !controls_allowed) { desired_torque_last = 0; rt_torque_last = 0; ts_last = ts; } if (violation) { tx = 0; } } // FORCE CANCEL: safety check only relevant when spamming the cancel button. // ensuring that only the cancel button press is sent (VAL 4) when controls are off. // This avoids unintended engagements while still allowing resume spam if ((addr == 1265) && !controls_allowed) { if ((GET_BYTES_04(to_send) & 0x7) != 4) { tx = 0; } } // 1 allows the message through return tx; } static int hyundai_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { int bus_fwd = -1; int addr = GET_ADDR(to_fwd); // forward cam to ccan and viceversa, except lkas cmd if (!relay_malfunction) { if (bus_num == 0) { bus_fwd = 2; } if ((bus_num == 2) && (addr != 832) && (addr != 1157)) { bus_fwd = 0; } } return bus_fwd; } static void hyundai_init(int16_t param) { UNUSED(param); controls_allowed = false; relay_malfunction_reset(); hyundai_legacy = false; } static void hyundai_legacy_init(int16_t param) { UNUSED(param); controls_allowed = false; relay_malfunction_reset(); hyundai_legacy = true; } const safety_hooks hyundai_hooks = { .init = hyundai_init, .rx = hyundai_rx_hook, .tx = hyundai_tx_hook, .tx_lin = nooutput_tx_lin_hook, .fwd = hyundai_fwd_hook, .addr_check = hyundai_rx_checks, .addr_check_len = sizeof(hyundai_rx_checks) / sizeof(hyundai_rx_checks[0]), }; const safety_hooks hyundai_legacy_hooks = { .init = hyundai_legacy_init, .rx = hyundai_rx_hook, .tx = hyundai_tx_hook, .tx_lin = nooutput_tx_lin_hook, .fwd = hyundai_fwd_hook, .addr_check = hyundai_legacy_rx_checks, .addr_check_len = sizeof(hyundai_legacy_rx_checks) / sizeof(hyundai_legacy_rx_checks[0]), };