Support Toyota DSU bypass on camera bus

This commit is contained in:
firestar5683
2026-10-23 19:59:49 -05:00
parent 276f3561ce
commit 4194390dc1
21 changed files with 626 additions and 35 deletions
@@ -142,6 +142,8 @@ class CarController(CarControllerBase):
self.brake_hold_active = False
def update(self, CC, CS, now_nanos):
from opendbc.car.toyota import dsu_bypass
bypass_long = dsu_bypass.enabled(self.CP)
_projection = self.learning_projection_observer
if _projection is not None:
_projection.begin(CC.actuators.torque, self.frame)
@@ -177,6 +179,14 @@ class CarController(CarControllerBase):
pedal_owner = pedal.configured(self.CP)
if pedal_owner:
filter_allowed = filter_allowed and bool(self.pedal_input is not None and self.pedal_input.update(now_nanos) and CS.pedal_ready)
if bypass_long:
preference = getattr(self, 'bypass_input', None)
filter_allowed = filter_allowed and bool(
preference is not None and preference.update(now_nanos) and CS.out.canValid and not CS.out.canTimeout and
CS.out.cruiseState.available and not CS.out.accFaulted and not CS.out.stockAeb and
not CS.out.steerFaultTemporary and not CS.out.steerFaultPermanent and not CS.out.doorOpen and
not CS.out.seatbeltUnlatched and not CS.out.gasPressed and not CS.out.brakePressed and
CS.out.gearShifter in (structs.CarState.GearShifter.drive, structs.CarState.GearShifter.sport))
long_active = CC.longActive and filter_allowed
if guarded_advert:
long_active = long_active and not (CS.out.seatbeltUnlatched or CS.out.gasPressed or CS.out.brakePressed)
@@ -184,9 +194,12 @@ class CarController(CarControllerBase):
stopping = actuators.longControlState == LongCtrlState.stopping
hud_control = CC.hudControl
pcm_cancel_cmd = CC.cruiseControl.cancel
if (rav4h_long or pedal_owner or advert_long or original_prius_long) and not filter_allowed and CS.out.cruiseState.enabled:
if (bypass_long or rav4h_long or pedal_owner or advert_long or original_prius_long) and not filter_allowed and CS.out.cruiseState.enabled:
pcm_cancel_cmd = True
lat_active = CC.latActive and abs(CS.out.steeringTorque) < MAX_USER_TORQUE
if dsu_bypass.configured(self.CP):
lat_active = lat_active and CS.out.canValid and not CS.out.canTimeout and CS.out.cruiseState.available and not (
CS.out.steerFaultTemporary or CS.out.steerFaultPermanent or CS.out.doorOpen)
if self.original_prius_filter:
lat_active = lat_active and CS.out.canValid and not CS.out.canTimeout and CS.out.cruiseState.available and not (
CS.out.accFaulted or CS.out.steerFaultTemporary or CS.out.steerFaultPermanent or CS.out.doorOpen)
@@ -329,7 +342,7 @@ class CarController(CarControllerBase):
if not should_resume and CS.out.cruiseState.standstill:
self.standstill_req = True
if (rav4h_long or pedal_owner or advert_long or original_prius_long) and (not filter_allowed or not long_active and CS.out.standstill):
if (bypass_long or rav4h_long or pedal_owner or advert_long or original_prius_long) and (not filter_allowed or not long_active and CS.out.standstill):
self.standstill_req = True
if pedal_owner and (self.CP.carFingerprint in pedal.RADAR_PEDAL_CARS or pedal_composite.configured(self.CP)) and not long_active:
@@ -348,7 +361,7 @@ class CarController(CarControllerBase):
# internal PCM gas command can get stuck unwinding from negative accel so we apply a generous rate limit
pcm_accel_cmd = actuators.accel if filter_allowed else 0.
if (self.prius_longitudinal or rav4h_long or advert_long) and not long_active:
if (bypass_long or self.prius_longitudinal or rav4h_long or advert_long) and not long_active:
pcm_accel_cmd = 0.
if long_active:
pcm_accel_cmd = rate_limit(pcm_accel_cmd, self.prev_accel, ACCEL_WINDDOWN_LIMIT, ACCEL_WINDUP_LIMIT)
@@ -419,7 +432,7 @@ class CarController(CarControllerBase):
self.permit_braking = True
self.standstill_req = True
if (rav4h_long or pedal_owner or advert_long or original_prius_long) and not long_active:
if (bypass_long or rav4h_long or pedal_owner or advert_long or original_prius_long) and not long_active:
pcm_accel_cmd = 0.
main_accel_cmd = 0. if self.CP.flags & ToyotaFlags.SECOC.value else pcm_accel_cmd
can_sends.append(toyotacan.create_accel_command(self.packer, main_accel_cmd, pcm_cancel_cmd, self.permit_braking, self.standstill_req, lead,
+24 -2
View File
@@ -2,7 +2,7 @@ import copy
import math
from opendbc.can import CANDefine, CANParser
from opendbc.car.toyota import pedal_composite, pedal, legacy_sdsu, advert_filter, legacy_advert_filter, prius_sdsu, prius_advert_filter
from opendbc.car.toyota import dsu_bypass, pedal_composite, pedal, legacy_sdsu, advert_filter, legacy_advert_filter, prius_sdsu, prius_advert_filter
from opendbc.car.toyota.lexus_is import LexusIsCANParser
from opendbc.car import Bus, DT_CTRL, create_button_events, structs
from opendbc.car.common.conversions import Conversions as CV
@@ -78,7 +78,8 @@ class CarState(CarStateBase):
self.pre_collision_2 = copy.copy(cp_cam.vl["PRE_COLLISION_2"])
ret = structs.CarState()
cp_acc = cp_cam if (self.CP.flags & ToyotaFlags.TSS2) and not (self.CP.flags & ToyotaFlags.RADAR_ACC) else cp
bypass = dsu_bypass.configured(self.CP)
cp_acc = cp_cam if bypass or (self.CP.flags & ToyotaFlags.TSS2) and not (self.CP.flags & ToyotaFlags.RADAR_ACC) else cp
if not self.CP.flags & ToyotaFlags.SECOC.value:
self.gvc = cp.vl["VSC1S07"]["GVC"]
@@ -177,6 +178,18 @@ class CarState(CarStateBase):
ret.cruiseState.speed = cp.vl["PCM_CRUISE_2"]["SET_SPEED"] * CV.KPH_TO_MS
cluster_set_speed = cp.vl["PCM_CRUISE_SM"]["UI_SET_SPEED"]
if bypass:
main = 'DSU_CRUISE' if self.CP.carFingerprint in dsu_bypass.ALT_MAIN_CARS else 'PCM_CRUISE_2'
stamp = cp.ts_nanos[main]['MAIN_ON']
ret.cruiseState.available &= bool(cp.main_valid and stamp > 0 and 0 <= cp._last_update_nanos - stamp <= 303_031_000)
stamp = cp.ts_nanos['EPS_STATUS']['LKA_STATE']
ret.steerFaultTemporary |= not (cp.eps_valid and stamp > 0 and 0 <= cp._last_update_nanos - stamp <= 400_000_000)
stamp = cp_cam.ts_nanos['ACC_CONTROL']['ACC_TYPE']
ret.accFaulted |= not (cp_cam.acc_valid and stamp > 0 and 0 <= cp_cam._last_update_nanos - stamp <= 303_031_000)
ret.accFaulted |= bool(cp_acc.vl['ACC_CONTROL']['ACC_MALFUNCTION'] or cp_acc.vl['ACC_CONTROL']['RADAR_DIRTY'] or cp.ambiguous or cp_cam.ambiguous)
self.acc_type = cp_acc.vl['ACC_CONTROL']['ACC_TYPE']
ret.stockFcw = bool(cp_acc.vl['PCS_HUD']['FCW'])
# UI_SET_SPEED is always non-zero when main is on, hide until first enable
is_metric = cp.vl["BODY_CONTROL_STATE_2"]["UNITS"] in (1, 2)
if ret.cruiseState.speed != 0:
@@ -317,6 +330,15 @@ class CarState(CarStateBase):
parent[Bus.pt] = pedal_composite.ParentPedalParser(parent[Bus.pt])
parent[Bus.alt] = pedal.PedalCANParser(DBC[CP.carFingerprint][Bus.pt], [('GAS_SENSOR', 50)], 0)
return parent
if dsu_bypass.configured(CP):
alternate = CP.carFingerprint in dsu_bypass.ALT_MAIN_CARS
main = 'DSU_CRUISE' if alternate else 'PCM_CRUISE_2'
kwargs = {'eps_length': 8 if CP.carFingerprint in dsu_bypass.EPS8_CARS or CP.carFingerprint == CAR.TOYOTA_PRIUS else 5,
'alternate_main': alternate}
return {
Bus.pt: dsu_bypass.BypassCANParser(DBC[CP.carFingerprint][Bus.pt], [(main, 10), ('EPS_STATUS', 25)], 0, **kwargs),
Bus.cam: dsu_bypass.BypassCANParser(DBC[CP.carFingerprint][Bus.pt], [('ACC_CONTROL', 33)], 2, **kwargs),
}
dbc_name = DBC[CP.carFingerprint][Bus.pt]
cam_messages = [("RSA1", math.nan)] if "RSA1" in CANDBC(dbc_name).name_to_msg else []
if CP.flags & ToyotaFlags.AUTO_BRAKE_HOLD and uses_toyota_auto_hold_aeb(CP):
@@ -0,0 +1,149 @@
"""Camera-bus DSU bypass identity and independent physical source ownership."""
from opendbc.can import CANParser
from opendbc.car import structs
from opendbc.car.toyota.toyotacan import toyota_checksum
from opendbc.car.toyota.values import CAR, ToyotaFlags
EPS5_CARS = frozenset(
(CAR.TOYOTA_AVALON, CAR.TOYOTA_HIGHLANDER, CAR.TOYOTA_RAV4, CAR.TOYOTA_RAV4H, CAR.TOYOTA_SIENNA, CAR.LEXUS_ES, CAR.LEXUS_NX, CAR.LEXUS_RX)
)
EPS8_CARS = frozenset((CAR.TOYOTA_AVALON_2019, CAR.TOYOTA_CAMRY, CAR.TOYOTA_CHR, CAR.LEXUS_LS, CAR.TOYOTA_PRIUS_RETROFIT))
ALT_MAIN_CARS = frozenset((CAR.LEXUS_IS, CAR.LEXUS_RC, CAR.LEXUS_GS_F))
PROFILES = dict.fromkeys(EPS5_CARS, 0x8849)
PROFILES.update(dict.fromkeys(EPS8_CARS, 0x9849))
PROFILES.update(
{
CAR.TOYOTA_PRIUS: 0x9842,
CAR.TOYOTA_COROLLA: 0x8958,
CAR.TOYOTA_MATRIX_RETROFIT: 0x8958,
CAR.TOYOTA_PRIUS_V: 0x8964,
CAR.LEXUS_CTH: 0x8964,
CAR.LEXUS_IS: 0xC84D,
CAR.LEXUS_RC: 0xC84D,
CAR.LEXUS_GS_F: 0xC949,
}
)
PROFILES.update({CAR.TOYOTA_RAV4: 0x8949, CAR.LEXUS_ES: 0x8949})
AUTO_RESUME_CARS = frozenset((CAR.TOYOTA_HIGHLANDER, CAR.TOYOTA_PRIUS_V, CAR.TOYOTA_RAV4H, CAR.TOYOTA_SIENNA))
WORDS = frozenset(word | stock for word in PROFILES.values() for stock in (0, 0x200))
def hardware_supported(candidate, fingerprint, car_fw):
if candidate not in PROFILES or any(address in frames for frames in fingerprint.values() for address in (0x201, 0x2FF, 0x2AA)):
return False
camera = fingerprint.get(2, {})
if not any(camera.get(address) == 8 for address in (0x343, 0x4CB)) or any(camera.get(address, 8) != 8 for address in (0x343, 0x4CB)):
return False
late_prius = candidate in (CAR.TOYOTA_PRIUS, CAR.TOYOTA_PRIUS_RETROFIT) and any(
fw.ecu == structs.CarParams.Ecu.fwdCamera and bytes(fw.fwVersion).startswith(b'8646F4705') for fw in car_fw
)
if candidate in (CAR.LEXUS_IS, CAR.TOYOTA_CAMRY) or late_prius:
if not (
(camera.get(0x343) == 8 and not any(0x343 in fingerprint.get(bus, {}) for bus in (0, 1)))
or (camera.get(0x4CB) == 8 and 0x4CB not in fingerprint.get(0, {}))
):
return False
if candidate == CAR.TOYOTA_MATRIX_RETROFIT:
# This is a manual fingerprint, not a firmware-catalog identity.
return not car_fw and all(fingerprint.get(0, {}).get(a) == length for a, length in ((0x260, 8), (0x262, 5), (0x224, 8), (0x1D2, 8), (0x1D3, 8)))
from opendbc.car.toyota.fingerprints import FW_VERSIONS
known = FW_VERSIONS[CAR.TOYOTA_PRIUS if candidate == CAR.TOYOTA_PRIUS_RETROFIT else candidate]
for ecu in (structs.CarParams.Ecu.eps, structs.CarParams.Ecu.fwdCamera, structs.CarParams.Ecu.fwdRadar):
if not any(key[0] == ecu for key in known) and candidate != CAR.TOYOTA_PRIUS_RETROFIT:
continue
observed = [fw for fw in car_fw if fw.ecu == ecu]
if not observed:
return False
for fw in observed:
version = bytes(fw.fwVersion)
allowed = (
(version == b'8965B47070' + bytes(6))
if ecu == structs.CarParams.Ecu.eps and candidate == CAR.TOYOTA_PRIUS_RETROFIT
else version in known.get((ecu, fw.address, fw.subAddress or None), ())
)
if fw.logging or fw.bus != 0 or fw.brand != 'toyota' or not allowed:
return False
return True
def apply(cp):
cp.flags |= int(ToyotaFlags.DSU_BYPASS)
cp.safetyConfigs[0].safetyParam = PROFILES[cp.carFingerprint]
cp.openpilotLongitudinalControl = True
cp.alphaLongitudinalAvailable = False
cp.autoResumeSng = cp.carFingerprint in AUTO_RESUME_CARS
if cp.carFingerprint in (CAR.TOYOTA_MATRIX_RETROFIT, CAR.TOYOTA_PRIUS_RETROFIT):
cp.dashcamOnly = False
def configured(cp):
if (
cp is None
or cp.brand != 'toyota'
or cp.carFingerprint not in PROFILES
or cp.notCar
or cp.passive
or cp.dashcamOnly
or cp.secOcRequired
or not cp.pcmCruise
or cp.steerControlType != structs.CarParams.SteerControlType.torque
or len(cp.safetyConfigs) != 1
):
return False
required = int(CAR[cp.carFingerprint].config.flags | ToyotaFlags.DSU_BYPASS)
allowed = required | int(ToyotaFlags.HYBRID | ToyotaFlags.ZSS)
cfg = cp.safetyConfigs[0]
return (
cp.flags & required == required
and not cp.flags & ~allowed
and not cp.alphaLongitudinalAvailable
and cp.autoResumeSng == (cp.openpilotLongitudinalControl and cp.carFingerprint in AUTO_RESUME_CARS)
and cfg.safetyModel == structs.CarParams.SafetyModel.toyota
and cfg.safetyParam == PROFILES[cp.carFingerprint] | (0 if cp.openpilotLongitudinalControl else 0x200)
)
def enabled(cp):
return configured(cp) and cp.openpilotLongitudinalControl
def prepare_stock(cp):
if not configured(cp):
return False
cp.openpilotLongitudinalControl = False
cp.autoResumeSng = False
cp.safetyConfigs[0].safetyParam = PROFILES[cp.carFingerprint] | 0x200
return True
class BypassCANParser(CANParser):
def __init__(self, *args, eps_length=8, alternate_main=False):
super().__init__(*args)
self.eps_length = eps_length
self.main_address = 0x365 if alternate_main else 0x1D3
self.main_length = 7 if alternate_main else 8
self.main_valid = False
self.eps_valid = False
self.acc_valid = False
self.ambiguous = False
def update(self, strings, sendcan=False):
batches = []
for stamp, frames in strings:
accepted = []
for address, data, bus in frames:
if address in (0x201, 0x2FF, 0x2AA):
self.ambiguous = True
if bus == self.bus:
if address in (self.main_address, 0x262, 0x343):
size = self.main_length if address == self.main_address else self.eps_length if address == 0x262 else 8
valid = len(data) == size and (address == 0x365 or data[-1] == toyota_checksum(address, None, bytearray(data)))
field = 'main_valid' if address == self.main_address else 'eps_valid' if address == 0x262 else 'acc_valid'
setattr(self, field, valid)
if not valid:
continue
accepted.append((address, data, bus))
batches.append((stamp, accepted))
return super().update(batches, sendcan)
@@ -252,6 +252,9 @@ class CarInterface(CarInterfaceBase):
from opendbc.car.toyota import prius_advert_filter
if prius_advert_filter.hardware_supported(candidate, fingerprint, car_fw):
prius_advert_filter.apply(ret)
from opendbc.car.toyota import dsu_bypass
if dsu_bypass.hardware_supported(candidate, fingerprint, car_fw):
dsu_bypass.apply(ret)
from opendbc.car.toyota import pedal
if ret.openpilotLongitudinalControl and pedal.hardware_supported(candidate, fingerprint, car_fw):
ret.flags |= int(ToyotaFlags.GAS_INTERCEPTOR)
@@ -6,6 +6,9 @@ CARS = frozenset((CAR.TOYOTA_CAMRY, CAR.TOYOTA_CHR, CAR.TOYOTA_AVALON_2019,
def enabled(cp):
from opendbc.car.toyota import dsu_bypass
if dsu_bypass.enabled(cp):
return cp.carFingerprint not in dsu_bypass.AUTO_RESUME_CARS
if cp is None or cp.carFingerprint not in CARS or not cp.openpilotLongitudinalControl:
return False
from opendbc.car.toyota import rav4h_filter, advert_filter, radar_filter
@@ -84,6 +84,7 @@ class ToyotaFlags(IntFlag):
AUTO_BRAKE_HOLD = 4096
RADAR_CAN_FILTER = 1 << 18 # Exact radar adapter; includes four-byte 2FF, distinct from legacy SDSU status
GAS_INTERCEPTOR = 1 << 17 # Exact paired pedal owner
DSU_BYPASS = 1 << 19 # Camera-bus DSU adapter; independent of smartDSU/filter
ZSS = 1 << 16 # Detected Prius secondary steering sensor; host-only, no safetyParam allocation
# deprecated flags
+45 -11
View File
@@ -110,12 +110,12 @@ static bool toyota_legacy_main_current(void) {
static bool toyota_legacy_advert_ready(void) {
bool ready = true;
if (toyota_pedal_composite || toyota_legacy_advert() || toyota_camera_2aa() || toyota_prius_filter()) {
if (toyota_dsu_bypass || toyota_pedal_composite || toyota_legacy_advert() || toyota_camera_2aa() || toyota_prius_filter()) {
static const uint32_t timeout[4] = {303031U, 10000000U, 3333334U, 400000U};
const uint32_t now = microsecond_timer_get();
ready = aol_rx_healthy() && toyota_aol_main && toyota_aol_drive && toyota_aol_doors_closed && toyota_aol_eps &&
(!toyota_camera_2aa() || !toyota_camera_2aa_ambiguous) &&
(!toyota_prius_filter() || !toyota_prius_sdsu_ambiguous);
(!toyota_prius_filter() || !toyota_prius_sdsu_ambiguous) && (!toyota_dsu_bypass || !toyota_bypass_ambiguous);
for (unsigned int i = 0U; i < 4U; i++) {
ready &= toyota_aol_seen[i] && (safety_get_ts_elapsed(now, toyota_aol_ts[i]) <= timeout[i]);
}
@@ -193,6 +193,19 @@ static bool toyota_get_quality_flag_valid(const CANPacket_t *msg) {
}
static void toyota_optional_rx_hook(const CANPacket_t *msg) {
if (toyota_dsu_bypass) {
if (msg_matches(msg, 0x343U, 2U)) {
// A malformed packet outside the required catalog cannot retain LONG.
toyota_bypass_control = false;
}
if ((msg->addr == 0x201U) || (msg->addr == 0x2FFU) || (msg->addr == 0x2AAU)) {
toyota_bypass_ambiguous = true;
}
if (msg_matches(msg, 0x283U, 2U)) {
const bool valid = (GET_LEN(msg) == 7U) && (toyota_get_checksum(msg) == toyota_compute_checksum(msg));
toyota_bypass_aeb = !valid || (GET_BIT(msg, 41U) && GET_BIT(msg, 23U));
}
}
if (toyota_prius_advert() && ((msg->addr == 0x201U) || (msg->addr == 0x2AAU) ||
((msg->addr == 0x2FFU) && (((msg->bus != 0U) && (msg->bus != 2U)) || (GET_LEN(msg) != 4U))))) {
toyota_prius_sdsu_ambiguous = true;
@@ -208,10 +221,10 @@ static void toyota_optional_rx_hook(const CANPacket_t *msg) {
toyota_camera_2aa_ambiguous = true;
}
const bool aol_checksum_source = (!toyota_alt_cruise && (msg->addr == 0x1D3U)) || (msg->addr == 0x262U) || (toyota_aol_secoc && (msg->addr == 0x127U));
const unsigned int aol_source_len = ((toyota_aol_rav4h_filter || toyota_alt_cruise || toyota_legacy_sdsu || toyota_legacy_advert() || (current_safety_param == 856U)) && (msg->addr == 0x262U)) ? 5U : 8U;
const unsigned int aol_source_len = (((toyota_dsu_bypass && toyota_bypass_eps5) || toyota_aol_rav4h_filter || toyota_alt_cruise || toyota_legacy_sdsu || toyota_legacy_advert() || (current_safety_param == 856U)) && (msg->addr == 0x262U)) ? 5U : 8U;
if (!aol_checksum_source || ((GET_LEN(msg) == aol_source_len) && (toyota_get_checksum(msg) == toyota_compute_checksum(msg)))) {
toyota_aol_observe(msg);
} else if ((toyota_pedal_composite || toyota_aol_enabled || toyota_legacy_advert() || toyota_camera_2aa() || toyota_prius_filter()) && (msg->bus == 0U)) {
} else if ((toyota_dsu_bypass || toyota_pedal_composite || toyota_aol_enabled || toyota_legacy_advert() || toyota_camera_2aa() || toyota_prius_filter()) && (msg->bus == 0U)) {
unsigned int index = 3U;
if (msg->addr == 0x1D3U) {
index = 0U;
@@ -244,9 +257,12 @@ static void toyota_optional_rx_hook(const CANPacket_t *msg) {
}
static void toyota_rx_hook(const CANPacket_t *msg) {
if (toyota_dsu_bypass && msg_matches(msg, 0x343U, 2U)) {
toyota_bypass_control = !GET_BIT(msg, 18U) && !GET_BIT(msg, 19U);
}
// Required sources have already passed RX length/checksum validation here.
// Optional ingress only runs for messages outside the required RX catalog.
if ((toyota_pedal_composite || toyota_legacy_advert() || toyota_camera_2aa() || toyota_prius_filter() || (current_safety_param == 856U)) && ((msg->addr == 0x262U) || (!toyota_alt_cruise && (msg->addr == 0x1D3U)))) {
if ((toyota_dsu_bypass || toyota_pedal_composite || toyota_legacy_advert() || toyota_camera_2aa() || toyota_prius_filter() || (current_safety_param == 856U)) && ((msg->addr == 0x262U) || (!toyota_alt_cruise && (msg->addr == 0x1D3U)))) {
toyota_aol_observe(msg);
}
if (toyota_alt_cruise && msg_matches(msg, 0x365U, 0U) && (GET_LEN(msg) == 7U)) {
@@ -398,7 +414,8 @@ static bool toyota_tx_hook(const CANPacket_t *msg) {
};
const bool filter_ready = (!(toyota_sdsu_status || toyota_pedal) || aol_rx_healthy()) && toyota_legacy_main_current() && toyota_legacy_advert_ready();
const bool filter_long_ready = filter_ready && (!toyota_sdsu_status || toyota_sdsu_control);
const bool filter_long_ready = filter_ready && (!toyota_sdsu_status || toyota_sdsu_control) &&
(!toyota_dsu_bypass || (toyota_bypass_control && !toyota_bypass_aeb));
bool tx = true;
if (toyota_pedal && (msg->addr == 0x200U)) {
@@ -580,14 +597,20 @@ static bool toyota_tx_hook(const CANPacket_t *msg) {
}
// Required physical DSU MAIN cannot be manufactured through host TX.
if ((toyota_legacy_sdsu || toyota_legacy_advert()) && toyota_alt_cruise && msg_matches(msg, 0x365U, 0U)) {
if ((toyota_dsu_bypass || toyota_legacy_sdsu || toyota_legacy_advert()) && toyota_alt_cruise && msg_matches(msg, 0x365U, 0U)) {
tx = false;
}
return tx;
}
static safety_config toyota_init(uint16_t param) {
const uint16_t base_param = toyota_pedal_parent_param(param);
const uint16_t base_param = toyota_bypass_base_param(toyota_pedal_parent_param(param));
toyota_dsu_bypass = toyota_bypass_param(param);
toyota_bypass_eps5 = toyota_dsu_bypass && ((param & 0x1000U) == 0U);
toyota_bypass_alt_main = toyota_dsu_bypass && ((param & 0x4000U) != 0U);
toyota_bypass_ambiguous = false;
toyota_bypass_control = false;
toyota_bypass_aeb = false;
toyota_pedal_composite = toyota_pedal_composite_param(param);
toyota_composite_pcm_gas = false;
toyota_composite_pedal_gas = false;
@@ -653,7 +676,7 @@ static safety_config toyota_init(uint16_t param) {
toyota_alt_brake = GET_FLAG(base_param, TOYOTA_PARAM_ALT_BRAKE);
toyota_stock_longitudinal = GET_FLAG(base_param, TOYOTA_PARAM_STOCK_LONGITUDINAL);
toyota_radar_diagnostics_allowed = !toyota_stock_longitudinal && !toyota_secoc && (base_param != 0x1049U) && (base_param != 0x1449U) && !toyota_eps8_sdsu && !toyota_legacy_sdsu && !toyota_legacy_advert_param(base_param) && !toyota_camera_2aa_param(base_param) && !toyota_prius_filter_param(base_param);
toyota_radar_diagnostics_allowed = !toyota_dsu_bypass && !toyota_stock_longitudinal && !toyota_secoc && (base_param != 0x1049U) && (base_param != 0x1449U) && !toyota_eps8_sdsu && !toyota_legacy_sdsu && !toyota_legacy_advert_param(base_param) && !toyota_camera_2aa_param(base_param) && !toyota_prius_filter_param(base_param);
toyota_lta = !toyota_legacy_advert_param(base_param) && GET_FLAG(base_param, TOYOTA_PARAM_LTA);
toyota_dbc_eps_torque_factor = base_param & TOYOTA_EPS_FACTOR;
@@ -742,6 +765,16 @@ static safety_config toyota_init(uint16_t param) {
} else {
ret.rx_checks_len = 4;
}
if (toyota_dsu_bypass) {
toyota_rx_checks[4].msg[0] = (CanMsgCheck){.addr = 0x343, .bus = 2U, .len = 8, .frequency = 33U,
.ignore_counter = true, .ignore_quality_flag = true};
toyota_rx_checks[5].msg[0] = (CanMsgCheck){.addr = 0x262, .bus = 0U, .len = toyota_bypass_eps5 ? 5 : 8, .frequency = 25U,
.ignore_counter = true, .ignore_quality_flag = true};
toyota_rx_checks[6].msg[0] = (CanMsgCheck){.addr = toyota_alt_cruise ? 0x365 : 0x1D3, .bus = 0U,
.len = toyota_alt_cruise ? 7 : 8, .frequency = 10U, .ignore_checksum = toyota_alt_cruise,
.ignore_counter = true, .ignore_quality_flag = true};
ret.rx_checks_len = 7;
}
if (toyota_pedal_composite) {
// Keep all parent checks, including smartDSU status, independently required.
toyota_rx_checks[5].msg[0] = (CanMsgCheck){.addr = 0x201, .bus = 0U, .len = 6, .frequency = 50U,
@@ -764,8 +797,9 @@ static safety_config toyota_init(uint16_t param) {
}
static bool toyota_fwd_hook(int bus_num, int addr) {
return (bus_num == 2) && (addr == 0x344) && toyota_hold_ready(ALT_EXP_TOYOTA_AEB_HOLD) &&
toyota_hold_host_seen && (safety_get_ts_elapsed(microsecond_timer_get(), toyota_hold_host_ts) <= 100000U);
return (toyota_dsu_bypass && !toyota_stock_longitudinal && (bus_num == 2) && (addr == 0x343)) ||
((bus_num == 2) && (addr == 0x344) && toyota_hold_ready(ALT_EXP_TOYOTA_AEB_HOLD) &&
toyota_hold_host_seen && (safety_get_ts_elapsed(microsecond_timer_get(), toyota_hold_host_ts) <= 100000U));
}
const safety_hooks toyota_hooks = {
+23 -8
View File
@@ -1,5 +1,20 @@
#pragma once
static bool toyota_dsu_bypass = false;
static bool toyota_bypass_eps5 = false;
static bool toyota_bypass_alt_main = false;
static bool toyota_bypass_ambiguous = false;
static bool toyota_bypass_control = false;
static bool toyota_bypass_aeb = false;
static bool toyota_bypass_param(uint16_t param) {
return (param == 0x8949U) || (param == 0x8B49U) || (param == 0x8849U) || (param == 0x8A49U) || (param == 0x9849U) || (param == 0x9A49U) || (param == 0x9842U) || (param == 0x9A42U) || (param == 0x8958U) || (param == 0x8B58U) || (param == 0x8964U) || (param == 0x8B64U) || (param == 0xC84DU) || (param == 0xCA4DU) || (param == 0xC949U) || (param == 0xCB49U);
}
static uint16_t toyota_bypass_base_param(uint16_t param) {
return toyota_bypass_param(param) ? (uint16_t)(param & 0x3FFU) : param;
}
static bool toyota_prius_sdsu_param(uint16_t param) {
return (param == 0x8042U) || (param == 0x8242U);
}
@@ -115,10 +130,10 @@ static uint8_t toyota_aol_permission(void) {
uint8_t permission = 0U;
if (fresh && toyota_aol_main && toyota_aol_drive && toyota_aol_doors_closed && toyota_aol_eps &&
(!toyota_camera_2aa() || !toyota_camera_2aa_ambiguous) &&
(!toyota_prius_filter() || !toyota_prius_sdsu_ambiguous)) {
(!toyota_prius_filter() || !toyota_prius_sdsu_ambiguous) && (!toyota_dsu_bypass || !toyota_bypass_ambiguous)) {
permission = request & 1U;
if (!toyota_aol_stock && controls_allowed && toyota_aol_belt && !gas_pressed && !brake_pressed &&
(!toyota_sdsu_status || toyota_sdsu_control)) {
(!toyota_sdsu_status || toyota_sdsu_control) && (!toyota_dsu_bypass || (toyota_bypass_control && !toyota_bypass_aeb))) {
permission |= request & 2U;
}
}
@@ -131,11 +146,11 @@ static void toyota_aol_observe(const CANPacket_t *msg) {
unsigned int expected_len = 8U;
if (toyota_alt_cruise && (msg->addr == main_addr)) {
expected_len = 7U;
} else if ((toyota_aol_rav4h_filter || toyota_alt_cruise || toyota_legacy_sdsu || toyota_legacy_advert() || (current_safety_param == 856U)) && (msg->addr == 0x262U)) {
} else if (((toyota_dsu_bypass && toyota_bypass_eps5) || toyota_aol_rav4h_filter || toyota_alt_cruise || toyota_legacy_sdsu || toyota_legacy_advert() || (current_safety_param == 856U)) && (msg->addr == 0x262U)) {
expected_len = 5U;
} else {
}
if ((toyota_pedal_composite || toyota_aol_enabled || toyota_legacy_advert() || toyota_camera_2aa() || toyota_prius_filter()) && (msg->bus == 0U) && (GET_LEN(msg) != expected_len)) {
if ((toyota_dsu_bypass || toyota_pedal_composite || toyota_aol_enabled || toyota_legacy_advert() || toyota_camera_2aa() || toyota_prius_filter()) && (msg->bus == 0U) && (GET_LEN(msg) != expected_len)) {
unsigned int index = 4U;
if (msg->addr == main_addr) {
index = 0U;
@@ -150,7 +165,7 @@ static void toyota_aol_observe(const CANPacket_t *msg) {
if (index < 4U) {
toyota_aol_seen[index] = false;
}
} else if ((toyota_pedal_composite || toyota_aol_enabled || toyota_legacy_advert() || toyota_camera_2aa() || toyota_prius_filter()) && (msg->bus == 0U) && (GET_LEN(msg) == expected_len)) {
} else if ((toyota_dsu_bypass || toyota_pedal_composite || toyota_aol_enabled || toyota_legacy_advert() || toyota_camera_2aa() || toyota_prius_filter()) && (msg->bus == 0U) && (GET_LEN(msg) == expected_len)) {
unsigned int index = 4U;
if (msg->addr == main_addr) {
index = 0U;
@@ -194,18 +209,18 @@ static void toyota_aol_configure(uint16_t param) {
toyota_prius_sdsu_ambiguous = false;
toyota_eps8_sdsu = (param == 0x8049U) || (param == 0x8249U) || (param == 0x8449U) || (param == 0x8649U);
toyota_legacy_sdsu = (param == 0x9158U) || (param == 0x9358U) || (param == 0x9149U) || (param == 0x9349U) || (param == 0x9164U) || (param == 0x9364U) || (param == 0xd149U) || (param == 0xd349U) || (param == 0xd04dU) || (param == 0xd24dU);
toyota_alt_cruise = (param == 0x424DU) || ((toyota_legacy_sdsu || toyota_legacy_advert_param(param)) && ((param & 0x4000U) != 0U));
toyota_alt_cruise = toyota_bypass_alt_main || (param == 0x424DU) || ((toyota_legacy_sdsu || toyota_legacy_advert_param(param)) && ((param & 0x4000U) != 0U));
toyota_aol_lta = (param == 0x2449U) || (param == 0x2649U) || (param == 0x449U) || (param == 0x649U) || (param == 0x1449U) || (param == 0x1649U) || (param == 0x8449U) || (param == 0x8649U);
toyota_pedal = (param == 0x2049U) || (param == 0x2249U) || (param == 0x2449U) || (param == 0x2649U);
toyota_aol_rav4h_filter = (param == 0x9049U) || (param == 0x9249U);
toyota_sdsu_status = toyota_aol_rav4h_filter || toyota_eps8_sdsu || toyota_legacy_sdsu || toyota_prius_sdsu_param(param);
toyota_aol_stock = (param == 0x2649U) || (param == 585U) || (param == 856U) || (param == 4681U) || (param == 0x9249U) || (param == 0x2249U) || (param == 0x424DU) || (param == 0x649U) || (param == 0x1649U) || (param == 0x8249U) || (param == 0x8649U) || ((toyota_legacy_sdsu || toyota_legacy_advert_param(param) || toyota_camera_2aa_param(param) || toyota_prius_filter_param(param)) && ((param & 0x200U) != 0U));
toyota_aol_stock = (toyota_dsu_bypass && ((param & 0x200U) != 0U)) || (param == 0x2649U) || (param == 585U) || (param == 856U) || (param == 4681U) || (param == 0x9249U) || (param == 0x2249U) || (param == 0x424DU) || (param == 0x649U) || (param == 0x1649U) || (param == 0x8249U) || (param == 0x8649U) || ((toyota_legacy_sdsu || toyota_legacy_advert_param(param) || toyota_camera_2aa_param(param) || toyota_prius_filter_param(param)) && ((param & 0x200U) != 0U));
#ifdef ALLOW_DEBUG
toyota_aol_secoc = (param == 2121U);
#else
toyota_aol_secoc = false;
#endif
toyota_aol_enabled = ((toyota_eps8_sdsu || toyota_legacy_sdsu || toyota_legacy_advert_param(param) || toyota_camera_2aa_param(param) || toyota_prius_filter_param(param)) && (alternative_experience == 32)) || (toyota_aol_lta && (alternative_experience == 32)) || (toyota_aol_stock && (alternative_experience == 32)) ||
toyota_aol_enabled = (toyota_dsu_bypass && (alternative_experience == 32)) || ((toyota_eps8_sdsu || toyota_legacy_sdsu || toyota_legacy_advert_param(param) || toyota_camera_2aa_param(param) || toyota_prius_filter_param(param)) && (alternative_experience == 32)) || (toyota_aol_lta && (alternative_experience == 32)) || (toyota_aol_stock && (alternative_experience == 32)) ||
#ifdef ALLOW_DEBUG
(toyota_aol_secoc && (alternative_experience == 32)) ||
#endif
@@ -0,0 +1,126 @@
"""Actual camera ACC, physical MAIN/EPS and native TX/forwarding ownership."""
import pytest
from opendbc.can import CANPacker
from opendbc.car import Bus, structs
from opendbc.car.toyota import dsu_bypass
from opendbc.car.toyota.values import CAR, DBC
from opendbc.safety.tests.libsafety import libsafety_py
from openpilot.starpilot.car.toyota.tests import test_rav4h_caller_native as helpers
source_matched_native = helpers.source_matched_native
REPRESENTATIVES = (CAR.TOYOTA_RAV4H, CAR.LEXUS_ES, CAR.TOYOTA_CAMRY, CAR.TOYOTA_PRIUS, CAR.TOYOTA_COROLLA, CAR.TOYOTA_PRIUS_V, CAR.LEXUS_IS, CAR.LEXUS_GS_F)
class Sources:
def __init__(self, candidate, stock=False):
self.candidate, self.stock = candidate, stock
self.safety = libsafety_py.libsafety
self.safety.init_tests()
self.safety.set_alternative_experience(32)
assert self.safety.set_safety_hooks(structs.CarParams.SafetyModel.toyota, dsu_bypass.PROFILES[candidate] | (0x200 if stock else 0)) == 0
self.safety.set_aol_test_heartbeat(True)
self.packer = CANPacker(DBC[candidate][Bus.pt])
self.now = 1_000_000
for _ in range(30):
self.tick()
def rx(self, frame):
a, d, b = frame
return bool(self.safety.safety_rx_hook(libsafety_py.make_CANPacket(a, b, d)))
def tick(self, fault=None, cruise=True):
self.now += 10_000
self.safety.set_timer(self.now)
fields = {
'cruise': cruise,
'sdsu': None,
'main': fault != 'main-off',
'eps': 9 if fault == 'eps-state' else 1,
'gear': 16 if fault == 'reverse' else 0,
'gas': fault == 'gas',
'brake': fault == 'brake',
}
frames = helpers.physical(self.packer, **fields)
if self.candidate in dsu_bypass.ALT_MAIN_CARS:
frames += [self.packer.make_can_msg('DSU_CRUISE', 0, {'MAIN_ON': fields['main'], 'SET_SPEED': 90})]
frames += [
self.packer.make_can_msg('ACC_CONTROL', 2, {'ACC_TYPE': 1, 'ACC_MALFUNCTION': fault == 'acc-fault'}),
self.packer.make_can_msg('PRE_COLLISION', 2, {'PRECOLLISION_ACTIVE': fault == 'aeb', 'FORCE': -20 if fault == 'aeb' else 0}),
]
selected = (
0x343
if fault and fault.startswith('acc-')
else (0x365 if self.candidate in dsu_bypass.ALT_MAIN_CARS else 0x1D3)
if fault and fault.startswith('main-')
else 0x262
)
for a, d, b in frames:
if fault and a == selected and b == (2 if selected == 0x343 else 0):
if fault.endswith('-drop'):
continue
if fault.endswith('-dlc'):
d = d[:-1]
elif fault.endswith('-crc'):
d = d[:-1] + bytes([d[-1] ^ 1])
accepted = self.rx((a, d, b))
if fault and fault.endswith('-crc') and a == selected and b == (2 if selected == 0x343 else 0):
assert not accepted
else:
assert accepted, (hex(a), d.hex(), b)
if fault == 'mixed-pedal':
self.rx((0x201, bytes(6), 0))
self.safety.safety_tick()
self.safety.aol_set_host_request(3)
def accel(self, value):
return self.rx_tx('ACC_CONTROL', {'ACCEL_CMD': value, 'PERMIT_BRAKING': 1, 'RELEASE_STANDSTILL': value != 0})
def rx_tx(self, name, fields):
a, d, b = self.packer.make_can_msg(name, 0, fields)
return bool(self.safety.safety_tx_hook(libsafety_py.make_CANPacket(a, b, d)))
def close(self):
self.safety.set_alternative_experience(0)
self.safety.set_safety_hooks(structs.CarParams.SafetyModel.noOutput, 0)
@pytest.mark.parametrize('candidate', REPRESENTATIVES)
@pytest.mark.parametrize('stock', (False, True))
def test_exact_native_health_permission_accel_and_forwarding(candidate, stock):
src = Sources(candidate, stock)
try:
assert src.safety.safety_config_valid()
assert src.safety.aol_get_permission_mask() == (1 if stock else 3)
assert src.accel(0.05) == (not stock)
assert src.safety.safety_fwd_hook(2, 0x343) == (0 if stock else -1)
assert src.safety.safety_fwd_hook(2, 0x4CB) == 0
assert src.safety.safety_fwd_hook(2, 0x283) == 0
finally:
src.close()
@pytest.mark.parametrize('candidate', REPRESENTATIVES)
@pytest.mark.parametrize(
'fault',
('acc-drop', 'acc-dlc', 'acc-crc', 'acc-fault', 'main-off', 'eps-drop', 'eps-dlc', 'eps-crc', 'eps-state', 'reverse', 'gas', 'brake', 'aeb', 'mixed-pedal'),
)
def test_independent_sources_and_physical_vetoes(candidate, fault):
src = Sources(candidate)
try:
assert src.accel(0.05)
for _ in range(41): # Strictly exceed the existing 400ms EPS lease.
src.tick(fault)
assert not src.accel(0.05), fault
if fault != 'mixed-pedal':
for _ in range(30):
src.tick(cruise=False)
for _ in range(30):
src.tick()
assert src.accel(0.05), fault
else:
src.tick(cruise=False)
src.tick()
assert not src.accel(0.05)
finally:
src.close()
@@ -179,7 +179,7 @@ int main() {
}
for (uint32_t word = 0U; word <= 65535U; ++word) {
const bool expected = (word == 0xB049U) || (word == 0xB249U) || (word == 0xD049U) || (word == 0xD249U) || (word == 0xC042U) || (word == 0xC242U) || (word == 0x8042U) || (word == 0x8242U) || (word == 0xC049U) || (word == 0xC249U) || (word == 73U) || (word == 585U) || (word == 856U) || (word == 4169U) || (word == 4681U) || (word == 2121U) || (word == 0x9049U) || (word == 0x9249U) || (word == 0x2449U) || (word == 0x2649U) || (word == 0x2049U) || (word == 0x2249U) || (word == 0x424DU) || (word == 0x449U) || (word == 0x649U) || (word == 0x1449U) || (word == 0x1649U) || (word == 0x8049U) || (word == 0x8249U) || (word == 0x8449U) || (word == 0x8649U) || (word == 0x9158U) || (word == 0x9358U) || (word == 0x9149U) || (word == 0x9349U) || (word == 0x9164U) || (word == 0x9364U) || (word == 0xd149U) || (word == 0xd349U) || (word == 0xd04dU) || (word == 0xd24dU) || (word == 0x9449U) || (word == 0x9549U) || (word == 0x9558U) || (word == 0x9564U) || (word == 0x9649U) || (word == 0x9749U) || (word == 0x9758U) || (word == 0x9764U) || (word == 0xD44DU) || (word == 0xD549U) || (word == 0xD64DU) || (word == 0xD749U);
const bool expected = (word == 0x8949U) || (word == 0x8B49U) || (word == 0x8849U) || (word == 0x8A49U) || (word == 0x9849U) || (word == 0x9A49U) || (word == 0x9842U) || (word == 0x9A42U) || (word == 0x8958U) || (word == 0x8B58U) || (word == 0x8964U) || (word == 0x8B64U) || (word == 0xC84DU) || (word == 0xCA4DU) || (word == 0xC949U) || (word == 0xCB49U) || (word == 0xB049U) || (word == 0xB249U) || (word == 0xD049U) || (word == 0xD249U) || (word == 0xC042U) || (word == 0xC242U) || (word == 0x8042U) || (word == 0x8242U) || (word == 0xC049U) || (word == 0xC249U) || (word == 73U) || (word == 585U) || (word == 856U) || (word == 4169U) || (word == 4681U) || (word == 2121U) || (word == 0x9049U) || (word == 0x9249U) || (word == 0x2449U) || (word == 0x2649U) || (word == 0x2049U) || (word == 0x2249U) || (word == 0x424DU) || (word == 0x449U) || (word == 0x649U) || (word == 0x1449U) || (word == 0x1649U) || (word == 0x8049U) || (word == 0x8249U) || (word == 0x8449U) || (word == 0x8649U) || (word == 0x9158U) || (word == 0x9358U) || (word == 0x9149U) || (word == 0x9349U) || (word == 0x9164U) || (word == 0x9364U) || (word == 0xd149U) || (word == 0xd349U) || (word == 0xd04dU) || (word == 0xd24dU) || (word == 0x9449U) || (word == 0x9549U) || (word == 0x9558U) || (word == 0x9564U) || (word == 0x9649U) || (word == 0x9749U) || (word == 0x9758U) || (word == 0x9764U) || (word == 0xD44DU) || (word == 0xD549U) || (word == 0xD64DU) || (word == 0xD749U);
auto toyota = status();
toyota.safety_mode = 2U;
toyota.safety_param = static_cast<uint16_t>(word);
+5
View File
@@ -6,6 +6,8 @@ from openpilot.starpilot.aol.policy import AolVehiclePolicy
from openpilot.starpilot.aol.intent import AolCardIntent, AOL_TOGGLE
AOL_WORDS = frozenset((
0x8949, 0x8B49,
0x8849, 0x8A49, 0x9849, 0x9A49, 0x9842, 0x9A42, 0x8958, 0x8B58, 0x8964, 0x8B64, 0xC84D, 0xCA4D, 0xC949, 0xCB49,
0x8042, 0x8242, 0xC042, 0xC242, 0xD049, 0xD249,
0xC049, 0xC249,
0x9449, 0x9549, 0x9558, 0x9564, 0x9649, 0x9749, 0x9758, 0x9764, 0xD44D, 0xD549, 0xD64D, 0xD749,
@@ -97,6 +99,9 @@ def stock_prius_retrofit_qualified(cp, *, marked_only=False):
def qualified(cp, *, marked_only=False):
from opendbc.car.toyota import dsu_bypass
if dsu_bypass.configured(cp):
return cp.alternativeExperience in ((32,) if marked_only else (0, 32))
from opendbc.car.toyota import pedal_composite
if pedal_composite.configured(cp):
return qualified(pedal_composite.parent_cp(cp), marked_only=marked_only)
+1 -1
View File
@@ -3,7 +3,7 @@
#include "selfdrive/pandad/aol_protocol.h"
inline bool toyota_aol_param(uint16_t param) {
return (param == 0xB049U) || (param == 0xB249U) || (param == 0xD049U || param == 0xD249U || param == 0xC042U || param == 0xC242U || param == 0x8042U || param == 0x8242U || param == 0xC049U) || (param == 0xC249U) || (param == 73U) || (param == 585U) || (param == 856U) || (param == 4169U) || (param == 4681U) || (param == 2121U) || (param == 0x9049U) || (param == 0x9249U) || (param == 0x2449U) || (param == 0x2649U) || (param == 0x2049U) || (param == 0x2249U) || (param == 0x424DU) || (param == 0x449U) || (param == 0x649U) || (param == 0x1449U) || (param == 0x1649U) || (param == 0x8049U) || (param == 0x8249U) || (param == 0x8449U) || (param == 0x8649U) || (param == 0x9158U) || (param == 0x9358U) || (param == 0x9149U) || (param == 0x9349U) || (param == 0x9164U) || (param == 0x9364U) || (param == 0xd149U) || (param == 0xd349U) || (param == 0xd04dU) || (param == 0xd24dU) || (param == 0x9449U) || (param == 0x9549U) || (param == 0x9558U) || (param == 0x9564U) || (param == 0x9649U) || (param == 0x9749U) || (param == 0x9758U) || (param == 0x9764U) || (param == 0xD44DU) || (param == 0xD549U) || (param == 0xD64DU) || (param == 0xD749U);
return (param == 0x8949U) || (param == 0x8B49U) || (param == 0x8849U) || (param == 0x8A49U) || (param == 0x9849U) || (param == 0x9A49U) || (param == 0x9842U) || (param == 0x9A42U) || (param == 0x8958U) || (param == 0x8B58U) || (param == 0x8964U) || (param == 0x8B64U) || (param == 0xC84DU) || (param == 0xCA4DU) || (param == 0xC949U) || (param == 0xCB49U) || (param == 0xB049U) || (param == 0xB249U) || (param == 0xD049U || param == 0xD249U || param == 0xC042U || param == 0xC242U || param == 0x8042U || param == 0x8242U || param == 0xC049U) || (param == 0xC249U) || (param == 73U) || (param == 585U) || (param == 856U) || (param == 4169U) || (param == 4681U) || (param == 2121U) || (param == 0x9049U) || (param == 0x9249U) || (param == 0x2449U) || (param == 0x2649U) || (param == 0x2049U) || (param == 0x2249U) || (param == 0x424DU) || (param == 0x449U) || (param == 0x649U) || (param == 0x1449U) || (param == 0x1649U) || (param == 0x8049U) || (param == 0x8249U) || (param == 0x8449U) || (param == 0x8649U) || (param == 0x9158U) || (param == 0x9358U) || (param == 0x9149U) || (param == 0x9349U) || (param == 0x9164U) || (param == 0x9364U) || (param == 0xd149U) || (param == 0xd349U) || (param == 0xd04dU) || (param == 0xd24dU) || (param == 0x9449U) || (param == 0x9549U) || (param == 0x9558U) || (param == 0x9564U) || (param == 0x9649U) || (param == 0x9749U) || (param == 0x9758U) || (param == 0x9764U) || (param == 0xD44DU) || (param == 0xD549U) || (param == 0xD64DU) || (param == 0xD749U);
}
inline constexpr AolSafetyProfile TOYOTA_AOL_PROFILE{2U, toyota_aol_param, true};
@@ -0,0 +1,19 @@
from opendbc.car.toyota.dsu_bypass import enabled
from openpilot.starpilot.car.toyota.prius_preferences import filter_preference_enabled
class BypassPreference:
def __init__(self, cp, params):
self.cp, self.params = cp, params
self.read_ns = 0
self.enabled = False
def update(self, now_ns):
if type(now_ns) is not int or now_ns <= 0 or now_ns < self.read_ns or not enabled(self.cp):
self.read_ns = 0
self.enabled = False
return False
if self.read_ns == 0 or now_ns - self.read_ns >= 250_000_000:
self.read_ns = now_ns
self.enabled = filter_preference_enabled(self.params)
return self.enabled
@@ -2,15 +2,17 @@
from opendbc.car import structs
from opendbc.car.toyota.values import CAR
from opendbc.car.toyota.rav4h_filter import configured
from opendbc.car.toyota import legacy_sdsu, legacy_advert_filter
from opendbc.car.toyota import dsu_bypass, legacy_sdsu, legacy_advert_filter
from openpilot.starpilot.aol.runtime import AXIS_MAX_AGE_NS, INTENT_MAX_AGE_NS, SAFETY_MAX_AGE_NS, native_inventory_matches_cp
def below_engage_lateral_allowed(cp, cs, intent, native, *, source_ns, now_ns, axis_session_id):
try:
bypass = dsu_bypass.configured(cp)
legacy_advert = legacy_advert_filter.configured(cp)
return bool((configured(cp) or legacy_advert) and
(cp.carFingerprint in (CAR.TOYOTA_HIGHLANDER, CAR.TOYOTA_AVALON) or legacy_sdsu.configured(cp) or legacy_advert) and cp.alternativeExperience == 32 and
return bool((bypass or configured(cp) or legacy_advert) and
(bypass or cp.carFingerprint in (CAR.TOYOTA_HIGHLANDER, CAR.TOYOTA_AVALON) or legacy_sdsu.configured(cp) or legacy_advert) and
cp.alternativeExperience == 32 and
cp.steerAtStandstill and cp.minSteerSpeed <= 0 and cp.minEnableSpeed > 0 and 0 <= cs.vEgo < cp.minEnableSpeed and
intent is not None and intent.settingsQualified and intent.allowedLatch and intent.lateralArmed and not intent.pauseLateral and
intent.producerSessionId and intent.carStateLogMonoTime == source_ns and
@@ -0,0 +1,109 @@
"""Dom bypass topology, unchanged tune projection and exact stock pairing."""
import pytest
from opendbc.car import structs
from opendbc.car.toyota import dsu_bypass
from opendbc.car.toyota.interface import CarInterface
from opendbc.car.toyota.values import CAR, ToyotaFlags
from openpilot.starpilot.car.toyota.aol import qualified
from openpilot.starpilot.car.toyota.tests.test_rav4h_caller_native import firmware
def acquired(candidate):
if candidate == CAR.TOYOTA_MATRIX_RETROFIT:
return []
fw = firmware(CAR.TOYOTA_PRIUS if candidate == CAR.TOYOTA_PRIUS_RETROFIT else candidate)
if candidate == CAR.TOYOTA_PRIUS_RETROFIT:
for row in fw:
if row.ecu == structs.CarParams.Ecu.eps:
row.fwVersion = b'8965B47070' + bytes(6)
return fw
def fingerprint(candidate, address=0x343):
pt = {0x260: 8, 0x262: 8 if candidate in dsu_bypass.EPS8_CARS or candidate == CAR.TOYOTA_PRIUS else 5, 0x224: 8, 0x226: 8, 0x1D2: 8, 0x1D3: 8}
return {0: pt, 1: {}, 2: {address: 8}}
def factory(candidate, address=0x343):
return CarInterface.get_params(candidate, fingerprint(candidate, address), acquired(candidate), False, False, False)
@pytest.mark.parametrize('candidate', sorted(dsu_bypass.PROFILES))
@pytest.mark.parametrize('address', (0x343, 0x4CB))
@pytest.mark.parametrize('stock', (False, True))
def test_exact_identity_current_tunes_and_stock_pair(candidate, address, stock):
fp = fingerprint(candidate, address)
cp = factory(candidate, address)
baseline = CarInterface.get_params(candidate, {**fp, 2: {}}, acquired(candidate), False, False, False)
assert dsu_bypass.configured(cp) and qualified(cp) and not cp.dashcamOnly and cp.pcmCruise
assert cp.flags & ToyotaFlags.DSU_BYPASS and not cp.flags & ToyotaFlags.LONG_FILTER
assert cp.safetyConfigs[0].safetyParam == dsu_bypass.PROFILES[candidate]
for field in (
'lateralTuning',
'longitudinalTuning',
'steerActuatorDelay',
'steerLimitTimer',
'longitudinalActuatorDelay',
'minEnableSpeed',
'minSteerSpeed',
'wheelSpeedFactor',
'mass',
'wheelbase',
'steerRatio',
'centerToFront',
):
assert cp.to_dict()[field] == baseline.to_dict()[field], field
if stock:
assert dsu_bypass.prepare_stock(cp) and dsu_bypass.configured(cp) and qualified(cp)
assert not cp.openpilotLongitudinalControl and cp.safetyConfigs[0].safetyParam == dsu_bypass.PROFILES[candidate] | 0x200
@pytest.mark.parametrize('candidate', (CAR.TOYOTA_CAMRY, CAR.LEXUS_IS, CAR.TOYOTA_PRIUS, CAR.TOYOTA_PRIUS_RETROFIT))
@pytest.mark.parametrize('address', (0x343, 0x4CB))
def test_native_bus_mirror_is_not_adapter_identity(candidate, address):
fp = fingerprint(candidate, address)
fw = acquired(candidate)
if candidate in (CAR.TOYOTA_PRIUS, CAR.TOYOTA_PRIUS_RETROFIT):
for row in fw:
if row.ecu == structs.CarParams.Ecu.fwdCamera:
row.fwVersion = b'8646F47050'
fp[0][address] = 8
assert not dsu_bypass.hardware_supported(candidate, fp, fw)
@pytest.mark.parametrize('kind', ('foreign-pedal', 'filter', 'sdsu', 'camera-dlc', 'unknown-eps', 'logging', 'wrong-fw-bus'))
def test_mixed_topology_or_unknown_firmware_does_not_enroll(kind):
fp = fingerprint(CAR.TOYOTA_RAV4H)
fw = acquired(CAR.TOYOTA_RAV4H)
if kind in ('foreign-pedal', 'filter', 'sdsu'):
fp[0][{'foreign-pedal': 0x201, 'filter': 0x2AA, 'sdsu': 0x2FF}[kind]] = 6 if kind == 'foreign-pedal' else 8
elif kind == 'camera-dlc':
fp[2][0x343] = 7
else:
row = next(f for f in fw if f.ecu == structs.CarParams.Ecu.eps)
if kind == 'unknown-eps':
row.fwVersion = b'unknown'
elif kind == 'logging':
row.logging = True
else:
row.bus = 2
assert not dsu_bypass.hardware_supported(CAR.TOYOTA_RAV4H, fp, fw)
@pytest.mark.parametrize('candidate', sorted(dsu_bypass.PROFILES))
def test_native_eps_and_brake_basis_matches_current_car(candidate):
fp = fingerprint(candidate)
baseline = CarInterface.get_params(candidate, {**fp, 2: {}}, acquired(candidate), False, False, False)
cp = factory(candidate)
assert cp.safetyConfigs[0].safetyParam & 0x1FF == baseline.safetyConfigs[0].safetyParam & 0x1FF
@pytest.mark.parametrize('candidate', sorted(dsu_bypass.PROFILES))
def test_dom_auto_resume_and_stop_timer_are_model_specific(candidate):
from opendbc.car.toyota import legacy_stop_timer
cp = factory(candidate)
assert cp.autoResumeSng == (candidate in dsu_bypass.AUTO_RESUME_CARS)
assert legacy_stop_timer.enabled(cp) == (candidate not in dsu_bypass.AUTO_RESUME_CARS)
assert dsu_bypass.prepare_stock(cp)
assert not cp.autoResumeSng and not legacy_stop_timer.enabled(cp)
@@ -0,0 +1,63 @@
"""Factory, Card, SelfdriveD, Controls, CI, C++ heartbeat and native bypass."""
import pytest
from opendbc.car.toyota import dsu_bypass
from opendbc.car.toyota.values import CAR
from openpilot.starpilot.car.toyota.tests import test_rav4h_caller_native as helpers
from openpilot.starpilot.car.toyota.tests.test_dsu_bypass import acquired, fingerprint
from openpilot.starpilot.car.toyota.tests.test_toyota_cpp_transport import host_heartbeat_bridge as host_heartbeat_bridge
source_matched_native = helpers.source_matched_native
daemon_gc_scope = helpers.daemon_gc_scope
REPRESENTATIVES = (CAR.TOYOTA_RAV4H, CAR.LEXUS_ES, CAR.TOYOTA_CAMRY, CAR.TOYOTA_PRIUS, CAR.TOYOTA_COROLLA, CAR.TOYOTA_PRIUS_V, CAR.LEXUS_IS, CAR.LEXUS_GS_F)
def run(candidate, stock, bridge, scenario='nominal', aol=True):
def physical(packer, **fields):
fields['sdsu'] = None
frames = helpers.physical(packer, **fields)
if candidate in dsu_bypass.ALT_MAIN_CARS:
frames += [
packer.make_can_msg('DSU_CRUISE', 0, {'MAIN_ON': fields.get('main', True), 'SET_SPEED': 90}),
packer.make_can_msg('PCM_CRUISE_ALT', 0, {'UI_SET_SPEED': 90}),
]
frames += [packer.make_can_msg('ACC_CONTROL', 2, {'ACC_TYPE': 1}), packer.make_can_msg('PRE_COLLISION', 2, {}), packer.make_can_msg('PCS_HUD', 2, {})]
return frames
def observe(tick, host, cs, cc, safety, native):
del tick, cs, cc, native
assert dsu_bypass.configured(host.CP)
assert safety.safety_fwd_hook(2, 0x343) == (0 if stock else -1)
assert safety.safety_fwd_hook(2, 0x283) == 0
fp = [(a, bytes(length), bus) for bus, messages in fingerprint(candidate).items() for a, length in messages.items()]
manual = candidate in (CAR.TOYOTA_MATRIX_RETROFIT, CAR.TOYOTA_PRIUS_RETROFIT)
helpers.run_case(
stock,
scenario,
candidate=candidate,
bridge=bridge,
input_clock=True,
sdsu_words=(dsu_bypass.PROFILES[candidate], dsu_bypass.PROFILES[candidate] | 0x200),
physical_frames=physical,
on_sample=observe,
firmware_override=acquired(candidate),
fingerprint_frames=fp,
manual_selection=manual,
manual_bypass=manual,
bypass_owner=True,
aol=aol,
brake_stop_check_start=355,
)
@pytest.mark.parametrize('candidate', sorted(dsu_bypass.PROFILES))
@pytest.mark.parametrize('stock', (False, True))
def test_each_bypass_identity_full_caller(candidate, stock, host_heartbeat_bridge):
run(candidate, stock, host_heartbeat_bridge)
@pytest.mark.parametrize('candidate', REPRESENTATIVES)
@pytest.mark.parametrize('scenario', ('physical', 'off', 'ordinary'))
def test_profile_physical_saved_off_and_ordinary_paths(candidate, scenario, host_heartbeat_bridge):
run(candidate, False, host_heartbeat_bridge, 'nominal' if scenario == 'ordinary' else scenario, aol=scenario != 'ordinary')
@@ -232,7 +232,7 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_RAV4H,
sdsu_words=(0x9049, 0x9249), alpha_long=False, disable_setting=None, physical_frames=None, angle=False,
firmware_override=None, assert_hybrid_release=False, sdsu_mirror=False, on_sample=None, camera_messages=False, filter_advert=False,
brake_pause_mps=None, native_rx_expected=None, on_state=None, fingerprint_frames=None, manual_selection=False, native_tx_expected=None,
manual_filter_length=None):
manual_filter_length=None, manual_bypass=False, bypass_owner=False):
SYNTHETIC_LAST.clear()
NATIVE_LAST.clear()
safety = libsafety_py.libsafety
@@ -252,7 +252,16 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_RAV4H,
params.put('AolBrakePauseSpeedMps', float(brake_pause_mps), block=True)
fw = firmware(candidate) if firmware_override is None else firmware_override
if manual_selection:
if manual_filter_length is not None:
if manual_bypass:
assert fingerprint_frames is not None
assert candidate in (CAR.TOYOTA_MATRIX_RETROFIT, CAR.TOYOTA_PRIUS_RETROFIT) and bypass_owner
assert any(bus == 2 and address in (0x343, 0x4CB) and len(data) == 8 for address, data, bus in fingerprint_frames)
assert not any(address in (0x201, 0x2FF, 0x2AA) for address, _, _ in fingerprint_frames)
if candidate == CAR.TOYOTA_MATRIX_RETROFIT:
assert not fw and firmware_override == []
else:
assert any(f.ecu == structs.CarParams.Ecu.eps and f.address == 0x7A1 and f.bus == 0 and bytes(f.fwVersion) == b'8965B47070' + bytes(6) for f in fw)
elif manual_filter_length is not None:
if candidate == CAR.TOYOTA_MATRIX_RETROFIT:
assert manual_filter_length in (4, 8) and not fw and firmware_override == []
else:
@@ -327,7 +336,7 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_RAV4H,
)
)
rows = {r.key: r for r in wheel.rows()}
if candidate == CAR.TOYOTA_RAV4H:
if candidate == CAR.TOYOTA_RAV4H and not bypass_owner:
assert 'Bookmark' in rows['wheel:LKASButtonControl'].choices
assert 'Toggle AOL' not in rows['wheel:LKASButtonControl'].choices
assert not any('Cancel' in k or 'MainCruise' in k for k in rows)
@@ -574,7 +583,7 @@ def run_case(stock, scenario='nominal', *, aol=True, candidate=CAR.TOYOTA_RAV4H,
assert not host.CP.openpilotLongitudinalControl and not cc.longActive
assert host.CP.safetyConfigs[0].safetyParam == sdsu_words[1]
else:
preference = host.CI.CC.prius_filter_input if candidate == CAR.TOYOTA_PRIUS else (
preference = host.CI.CC.bypass_input if bypass_owner else host.CI.CC.prius_filter_input if candidate == CAR.TOYOTA_PRIUS else (
host.CI.CC.advert_filter_input if filter_advert or fingerprint_frames is not None and any(
address == 0x2AA for address, _, _ in fingerprint_frames) else host.CI.CC.rav4h_filter_input)
assert preference is not None and not preference.update(time.monotonic_ns())
@@ -175,6 +175,14 @@ def configure_controller(CI, params):
prepare_rav4h_stock(cp)
elif controller is not None:
controller.rav4h_filter_input = Rav4hFilterPreference(cp, params)
from opendbc.car.toyota import dsu_bypass
if dsu_bypass.enabled(cp):
from openpilot.starpilot.car.toyota.bypass_preferences import BypassPreference
from openpilot.starpilot.car.toyota.prius_preferences import filter_preference_enabled
if not filter_preference_enabled(params):
dsu_bypass.prepare_stock(cp)
elif controller is not None:
controller.bypass_input = BypassPreference(cp, params)
from opendbc.car.toyota import advert_filter
if advert_filter.enabled(cp):
from openpilot.starpilot.car.toyota.advert_preferences import AdvertFilterPreference
@@ -206,6 +206,8 @@ class VehicleStartupPreferences:
prepare_advert_filter_stock(cp)
from opendbc.car.toyota.angle_control import prepare_stock as prepare_angle_stock
prepare_angle_stock(cp)
from opendbc.car.toyota.dsu_bypass import prepare_stock as prepare_bypass_stock
prepare_bypass_stock(cp)
self._prepare_bolt(cp, fingerprints)
prepare_disable_longitudinal(cp, self.disable_bolt_long)
self._prepare_camera_pedal(cp)
@@ -247,6 +249,8 @@ class VehicleStartupPreferences:
prepare_advert_filter_stock(cp)
from opendbc.car.toyota.angle_control import prepare_stock as prepare_angle_stock
prepare_angle_stock(cp)
from opendbc.car.toyota.dsu_bypass import prepare_stock as prepare_bypass_stock
prepare_bypass_stock(cp)
self._prepare_bolt(cp)
prepare_disable_longitudinal(cp, self.disable_bolt_long)
self._prepare_camera_pedal(cp)
+1 -1
View File
@@ -1 +1 @@
{"protected_inventory_sha256": "de3ffa5eadfc93f3696ceba58a0260f2578fdc8f99862f6004354df661a83ba4", "schema_version": 1}
{"protected_inventory_sha256": "b9097bfbbea2a3eae03349fb84e39dda591f5f60678075d491ef6e4cf1aa3f97", "schema_version": 1}
+6
View File
@@ -40,6 +40,9 @@ AOL_LIFECYCLE_TARGETS = (
'openpilot/starpilot/car/toyota/tests/test_rav4h_pedal_composite_callers.py',
'opendbc_repo/opendbc/safety/tests/test_toyota_rav4h_pedal_composite.py',
'opendbc_repo/opendbc/safety/tests/test_toyota_rav4h_pedal_order.py',
'openpilot/starpilot/car/toyota/tests/test_dsu_bypass.py',
'openpilot/starpilot/car/toyota/tests/test_dsu_bypass_callers.py',
'opendbc_repo/opendbc/safety/tests/test_toyota_dsu_bypass.py',
'opendbc_repo/opendbc/safety/tests/test_toyota_radar_lta_pedal.py',
'opendbc_repo/opendbc/safety/tests/test_toyota_prius_retrofit_advert.py',
'openpilot/starpilot/aol/tests/test_crossmake_native_admission.py',
@@ -242,6 +245,9 @@ RELEASE_TARGETS = (
'openpilot/starpilot/car/toyota/tests/test_rav4h_pedal_composite_callers.py',
'opendbc_repo/opendbc/safety/tests/test_toyota_rav4h_pedal_composite.py',
'opendbc_repo/opendbc/safety/tests/test_toyota_rav4h_pedal_order.py',
'openpilot/starpilot/car/toyota/tests/test_dsu_bypass.py',
'openpilot/starpilot/car/toyota/tests/test_dsu_bypass_callers.py',
'opendbc_repo/opendbc/safety/tests/test_toyota_dsu_bypass.py',
'opendbc_repo/opendbc/safety/tests/test_toyota_radar_lta_pedal.py',
'opendbc_repo/opendbc/safety/tests/test_toyota_prius_retrofit_advert.py',
'opendbc_repo/opendbc/car/hyundai/tests/test_long_cp_copy.py',