Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file removed board/obj/.placeholder
Empty file.
1 change: 1 addition & 0 deletions board/safety.h
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ int set_safety_hooks(uint16_t mode, uint16_t param) {
regen_braking = false;
regen_braking_prev = false;
cruise_engaged_prev = false;
cruise_speed_set = false;
vehicle_moving = false;
acc_main_on = false;
cruise_button_prev = 0;
Expand Down
8 changes: 7 additions & 1 deletion board/safety/safety_hyundai.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ static void hyundai_rx_hook(const CANPacket_t *to_push) {
hyundai_common_cruise_state_check(cruise_engaged);
}

//// SCC11 is on bus 2 for camera-based SCC cars, bus 0 on all others
if ((addr == 0x420) && (((bus == 0) && !hyundai_camera_scc) || ((bus == 2) && hyundai_camera_scc))) {
cruise_speed_set = (GET_BYTE(to_push, 1));
}

if (bus == 0) {
if (addr == 0x251) {
int torque_driver_new = (GET_BYTES(to_push, 0, 2) & 0x7ffU) - 1024U;
Expand Down Expand Up @@ -271,7 +276,8 @@ static bool hyundai_tx_hook(const CANPacket_t *to_send) {
if ((addr == 0x4F1) && !hyundai_longitudinal) {
int button = GET_BYTE(to_send, 0) & 0x7U;

bool allowed_resume = (button == 1) && controls_allowed;
bool allowed_resume = ((button == 1) && controls_allowed && hyundai_pause_resune_btn) ||
((button == 1) && controls_allowed && (!hyundai_pause_resune_btn && cruise_speed_set));
bool allowed_cancel = (button == 4) && cruise_engaged_prev;
if (!(allowed_resume || allowed_cancel)) {
tx = false;
Expand Down
23 changes: 18 additions & 5 deletions board/safety/safety_hyundai_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const int HYUNDAI_PARAM_LONGITUDINAL = 4;
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_PAUSE_RESUME_BTN = 256;

const uint8_t HYUNDAI_PREV_BUTTON_SAMPLES = 8; // roughly 160 ms
const uint32_t HYUNDAI_STANDSTILL_THRSLD = 12; // 0.375 kph
Expand All @@ -25,6 +26,8 @@ bool hyundai_longitudinal = false;
bool hyundai_camera_scc = false;
bool hyundai_canfd_hda2 = false;
bool hyundai_alt_limits = false;
bool hyundai_pause_resune_btn = false;
bool hyundai_main_on_pressed = false;
uint8_t hyundai_last_button_interaction; // button messages since the user pressed an enable button

uint16_t hyundai_canfd_crc_lut[256];
Expand All @@ -35,6 +38,7 @@ void hyundai_common_init(uint16_t param) {
hyundai_camera_scc = GET_FLAG(param, HYUNDAI_PARAM_CAMERA_SCC);
hyundai_canfd_hda2 = GET_FLAG(param, HYUNDAI_PARAM_CANFD_HDA2);
hyundai_alt_limits = GET_FLAG(param, HYUNDAI_PARAM_ALT_LIMITS);
hyundai_pause_resune_btn = GET_FLAG(param, HYUNDAI_PARAM_PAUSE_RESUME_BTN);

hyundai_last_button_interaction = HYUNDAI_PREV_BUTTON_SAMPLES;

Expand Down Expand Up @@ -70,16 +74,25 @@ void hyundai_common_cruise_buttons_check(const int cruise_button, const bool mai
}

if (hyundai_longitudinal) {
// enter controls on falling edge of resume or set
// enter controls on falling edge of resume or set or on rising edge of main the first time for cars with pause/resume
bool set = (cruise_button != HYUNDAI_BTN_SET) && (cruise_button_prev == HYUNDAI_BTN_SET);
bool res = (cruise_button != HYUNDAI_BTN_RESUME) && (cruise_button_prev == HYUNDAI_BTN_RESUME);
if (set || res) {
bool res = (cruise_button != HYUNDAI_BTN_RESUME) && (cruise_button_prev == HYUNDAI_BTN_RESUME) && !hyundai_pause_resune_btn;
bool main = (main_button && !hyundai_main_on_pressed && hyundai_pause_resune_btn);
if (set || res || main) {
controls_allowed = true;
if (main) {
hyundai_main_on_pressed = true;
}
}

// exit controls on cancel press
if (cruise_button == HYUNDAI_BTN_CANCEL) {
controls_allowed = false;
if (hyundai_pause_resune_btn) {
// cancel is a pause/resume button on those cars
controls_allowed = !controls_allowed;
} else {
// exit controls on cancel press on those cars
controls_allowed = false;
}
}

cruise_button_prev = cruise_button;
Expand Down
1 change: 1 addition & 0 deletions board/safety_declarations.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ bool vehicle_moving = false;
bool acc_main_on = false; // referred to as "ACC off" in ISO 15622:2018
int cruise_button_prev = 0;
bool safety_rx_checks_invalid = false;
bool cruise_speed_set = false;

// for safety modes with torque steering control
int desired_torque_last = 0; // last desired steer torque
Expand Down
1 change: 1 addition & 0 deletions python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ class Panda:
FLAG_HYUNDAI_CANFD_ALT_BUTTONS = 32
FLAG_HYUNDAI_ALT_LIMITS = 64
FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING = 128
FLAG_HYUNDAI_PAUSE_RESUME_BTN = 256

FLAG_TESLA_POWERTRAIN = 1
FLAG_TESLA_LONG_CONTROL = 2
Expand Down
4 changes: 4 additions & 0 deletions tests/libpanda/safety_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ bool safety_config_valid() {
return true;
}

void set_cruise_speed_set(bool s) {
cruise_speed_set = s;
}

void set_controls_allowed(bool c){
controls_allowed = c;
}
Expand Down
2 changes: 2 additions & 0 deletions tests/libpanda/safety_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

def setup_safety_helpers(ffi):
ffi.cdef("""
void set_cruise_speed_set(bool s);
void set_controls_allowed(bool c);
bool get_controls_allowed(void);
bool get_longitudinal_allowed(void);
Expand Down Expand Up @@ -53,6 +54,7 @@ def setup_safety_helpers(ffi):
""")

class PandaSafety(Protocol):
def set_cruise_speed_set(self, s: bool) -> None: ...
def set_controls_allowed(self, c: bool) -> None: ...
def get_controls_allowed(self) -> bool: ...
def get_longitudinal_allowed(self) -> bool: ...
Expand Down
28 changes: 0 additions & 28 deletions tests/misra/suppressions.txt

This file was deleted.

3 changes: 2 additions & 1 deletion tests/safety/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,8 @@ def test_tx_hook_on_wrong_safety_mode(self):
msg = make_msg(bus, addr)
self.safety.set_controls_allowed(1)
# TODO: this should be blocked
if current_test in ["TestNissanSafety", "TestNissanSafetyAltEpsBus", "TestNissanLeafSafety"] and [addr, bus] in self.TX_MSGS:
if current_test in ["TestNissanSafety", "TestNissanSafetyAltEpsBus", "TestNissanLeafSafety", \
"TestHyundaiLongitudinalSafetyPauseResumeBtn", "TestHyundaiLongitudinalSafety"] and [addr, bus] in self.TX_MSGS:
continue
self.assertFalse(self._tx(msg), f"transmit of {addr=:#x} {bus=} from {test_name} during {current_test} was allowed")

Expand Down
63 changes: 55 additions & 8 deletions tests/safety/hyundai_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,31 @@ class HyundaiButtonBase:
def test_button_sends(self):
"""
Only RES and CANCEL buttons are allowed
- RES allowed while controls allowed
- RES allowed while controls allowed and cruise speed set for CAN cars without pause/resume button, allowed while controls allowed otherwise
- 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.assertFalse(self._tx(self._button_msg(Buttons.SET, bus=self.BUTTONS_TX_BUS)))
if "canfd" not in self.__class__.__name__.lower() and "pauseresume" not in self.__class__.__name__.lower():
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.safety.set_cruise_speed_set(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.safety.set_cruise_speed_set(1)
self.assertTrue(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)))
else:
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.assertFalse(self._tx(self._button_msg(Buttons.SET, bus=self.BUTTONS_TX_BUS)))

for enabled in (True, False):
self._rx(self._pcm_status_msg(enabled))
Expand Down Expand Up @@ -132,6 +147,38 @@ def test_cancel_button(self):
self._rx(self._button_msg(Buttons.CANCEL))
self.assertFalse(self.safety.get_controls_allowed())

def test_set_resume_buttons_pause_resume(self):
"""
SET enters controls allowed on their falling edge.
"""
for btn_prev in range(8):
for btn_cur in range(8):
if btn_cur == Buttons.CANCEL or btn_prev == Buttons.CANCEL:
continue
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_prev == Buttons.SET

self._rx(self._button_msg(btn_cur))
self.assertEqual(should_enable, self.safety.get_controls_allowed())

def test_cancel_button_pause_resume(self):
"""
CANCEL is a pause/resume button
"""
self.safety.set_controls_allowed(1)
self._rx(self._button_msg(Buttons.CANCEL))
self.assertFalse(self.safety.get_controls_allowed())
self.safety.set_controls_allowed(0)
self._rx(self._button_msg(Buttons.CANCEL))
self.assertTrue(self.safety.get_controls_allowed())

def test_tester_present_allowed(self):
"""
Ensure tester present diagnostic message is allowed to keep ECU knocked out
Expand Down
25 changes: 25 additions & 0 deletions tests/safety/test_hyundai.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ def setUp(self):
self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI, Panda.FLAG_HYUNDAI_CAMERA_SCC)
self.safety.init_tests()

class TestHyundaiPauseResumeBtn(TestHyundaiSafety):
def setUp(self):
self.packer = CANPackerPanda("hyundai_kia_generic")
self.safety = libpanda_py.libpanda
self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI, Panda.FLAG_HYUNDAI_PAUSE_RESUME_BTN)
self.safety.init_tests()

class TestHyundaiLegacySafety(TestHyundaiSafety):
def setUp(self):
Expand Down Expand Up @@ -167,6 +173,7 @@ def _user_gas_msg(self, gas):
values = {"CR_Vcu_AccPedDep_Pos": gas}
return self.packer.make_can_msg_panda("E_EMS11", 0, values, fix_checksum=checksum)


class 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]]

Expand Down Expand Up @@ -211,6 +218,24 @@ def test_no_aeb_scc12(self):
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_set_resume_buttons_pause_resume(self):
pass

def test_cancel_button_pause_resume(self):
pass

class TestHyundaiLongitudinalSafetyPauseResumeBtn(TestHyundaiLongitudinalSafety):
def setUp(self):
self.packer = CANPackerPanda("hyundai_kia_generic")
self.safety = libpanda_py.libpanda
self.safety.set_safety_hooks(Panda.SAFETY_HYUNDAI, Panda.FLAG_HYUNDAI_LONG | Panda.FLAG_HYUNDAI_PAUSE_RESUME_BTN)
self.safety.init_tests()

def test_set_resume_buttons(self):
pass

def test_cancel_button(self):
pass

if __name__ == "__main__":
unittest.main()
Loading