Revert "monitor chestnut USB in hardwared (#38741)" (#38744)

This reverts commit 7d5596d5c3527f274a3615181f52095909ffb8e9.

(cherry picked from commit c9f1602040149be793ea3d3e17a5fd8d915408c8)
This commit is contained in:
Daniel Koepping
2026-09-01 11:13:30 -07:00
committed by Comma Device
parent 5f4e447a3f
commit b66b3af59c
4 changed files with 14 additions and 149 deletions
-1
View File
@@ -2593,7 +2593,6 @@ struct Event {
clocks @35 :Clocks;
deviceState @6 :DeviceState;
chestnutState @152 :ChestnutState;
chestnutGpuState @153 :ChestnutState;
logMessage @18 :Text;
errorLogMessage @85 :Text;
-1
View File
@@ -26,7 +26,6 @@ _services: dict[str, tuple] = {
"temperatureSensor": (True, 2., 200),
"deviceState": (True, 2., 1),
"chestnutState": (True, 10., 10),
"chestnutGpuState": (False, 10.),
"touch": (True, 20., 1),
"can": (True, 100., 2053, QueueSize.BIG), # decimation gives ~3 msgs in a full segment
"controlsState": (True, 100., 10, QueueSize.MEDIUM),
@@ -1,124 +0,0 @@
import struct
from contextlib import suppress
import usb1
import openpilot.cereal.messaging as messaging
from openpilot.cereal.services import SERVICE_LIST
from openpilot.common.hardware.usb import CHESTNUT_USB_IDS
USB_TIMEOUT_MS = 100
PCIE_LTSSM_ADDRESS = 0xB450
class ChestnutUsb:
def __init__(self):
self.context: usb1.USBContext | None = None
self.handle = None
def close(self) -> None:
handle, context = self.handle, self.context
self.handle = None
self.context = None
with suppress(Exception):
if handle is not None:
handle.close()
with suppress(Exception):
if context is not None:
context.close()
def connect(self) -> bool:
if self.handle is not None:
return True
context = usb1.USBContext()
for vendor_id, product_id in CHESTNUT_USB_IDS:
handle = context.openByVendorIDAndProductID(vendor_id, product_id, skip_on_error=True)
if handle is not None:
self.context = context
self.handle = handle
return True
context.close()
return False
def _read(self, request: int, value: int, length: int) -> bytes:
if self.handle is None:
raise usb1.USBErrorNoDevice
raw = bytes(self.handle.controlRead(0xC0, request, value, 0, length, timeout=USB_TIMEOUT_MS))
if len(raw) != length:
raise ValueError(f"short chestnut USB response: {len(raw)}/{length}")
return raw
def read_ina(self) -> tuple[int, int, bool]:
return struct.unpack('<Hh?', self._read(0xC0, 0, 5))
def read_pcie_ltssm(self) -> int:
return self._read(0xE4, PCIE_LTSSM_ADDRESS, 1)[0]
class ChestnutMonitoring:
def __init__(self, usb: ChestnutUsb | None = None):
self.usb = usb or ChestnutUsb()
self.gpu_state = None
self.seen = False
self.enabled = False
self.usb_failed = False
def set_enabled(self, enabled: bool) -> None:
if self.enabled == enabled:
return
self.enabled = enabled
self.usb.close()
self.usb_failed = False
def retry(self) -> None:
self.usb_failed = False
def model_alive(self, sm: messaging.SubMaster, now: float) -> bool:
modeld = next((p for p in sm['managerState'].processes if p.name == 'modeld'), None)
if modeld is not None and modeld.shouldBeRunning and not modeld.running:
return False
recv_time = sm.recv_time['chestnutGpuState']
return recv_time > 0. and now - recv_time < 10. / SERVICE_LIST['chestnutGpuState'].frequency
def update_gpu_state(self, sm: messaging.SubMaster, now: float) -> None:
if sm.updated['chestnutGpuState']:
self.gpu_state = sm['chestnutGpuState'] if sm.valid['chestnutGpuState'] else None
elif not self.model_alive(sm, now):
self.gpu_state = None
def update(self, sm: messaging.SubMaster, now: float, model_loading: bool = False):
self.update_gpu_state(sm, now)
return self.build_message(model_loading)
def build_message(self, model_loading: bool = False):
if not self.enabled:
return None
msg = messaging.new_message('chestnutState')
if self.gpu_state is not None:
msg.chestnutState = self.gpu_state
state = msg.chestnutState
if self.usb_failed:
return msg if self.seen else None
try:
if not self.usb.connect():
self.usb_failed = True
return msg if self.seen else None
self.seen = True
voltage, current, fault = self.usb.read_ina()
pcie_ltssm = self.usb.read_pcie_ltssm()
state.supplyVoltage = voltage
state.supplyCurrent = current
state.supplyFault = fault
state.pcieLtssm = pcie_ltssm
msg.valid = True
except Exception as e:
if not model_loading or not isinstance(e, usb1.USBErrorTimeout):
self.usb.close()
self.usb_failed = True
return msg
+14 -23
View File
@@ -21,13 +21,14 @@ class ChestnutStatus:
self.power_lost = False
self.power_restored = False
self.link_failures = 0
self.model_loading_seen = False
self.model_attempted = False
self.overheated = False
self.usb_seen = False
self.usb_failed = False
def update(self, offroad: bool, branch: str, usb_state: list[dict], firmware_failed: bool,
model_active: bool | None, state, usb_failed: bool, set_alert) -> None:
model_loading: bool, model_active: bool | None, state, set_alert) -> None:
detected = [d for d in usb_state if is_chestnut_usb_id(d["vendorId"], d["productId"], include_bootloader=True)]
devices = [d for d in detected if is_chestnut_usb_id(d["vendorId"], d["productId"])]
firmware_ok = len(devices) == 1 and devices[0]["product"] == CHESTNUT_USB_PRODUCT
@@ -39,15 +40,16 @@ class ChestnutStatus:
self.power_lost = False
self.power_restored = False
self.link_failures = 0
self.model_loading_seen = False
self.model_attempted = False
self.usb_seen = firmware_ok
self.usb_failed = False
self.model_attempted |= model_active is not None
self.model_loading_seen |= model_loading
self.model_attempted |= self.model_loading_seen and not model_loading and model_active is not None
if not offroad:
self.usb_seen |= firmware_ok
self.usb_failed = not offroad and self.usb_seen and (not firmware_ok or usb_failed)
if not offroad and self.usb_seen and not firmware_ok:
self.usb_failed = True
if not offroad and state is not None:
powered = state.supplyVoltage >= CHESTNUT_POWERED_VOLTAGE
@@ -78,28 +80,17 @@ class ChestnutStatus:
release = branch in CHESTNUT_RELEASE_BRANCHES
missing = self.usb_failed or (offroad and release and time.monotonic() - self.started > 10. and len(detected) != 1)
slow_usb = offroad and len(devices) == 1 and devices[0]["speedMbps"] < 5000
update_failed = offroad and firmware_failed
compiled = firmware_ok and chestnut_compiled()
uncompiled = offroad and firmware_ok and not compiled
set_alert("Offroad_ChestnutBranch", not release and len(devices) == 1)
set_alert("Offroad_ChestnutNotDetected", missing)
set_alert("Offroad_ChestnutOverheated", self.overheated, f"{state.tempC:.0f} °C" if state is not None else None)
set_alert("Offroad_ChestnutUsbSlow", slow_usb, f"{devices[0]['speedMbps']} Mbps" if slow_usb else None)
if self.power_lost:
pcie_alert = ("Chestnut power restored. 12V is stable again, cycle ignition." if self.power_restored else
"Chestnut power disconnected. Check 12V connection, then cycle ignition." if self.power_unavailable else
"Chestnut power lost. Possibly caused by an engine-crank voltage drop. Check 12V connection, then cycle ignition.")
else:
pcie_alert = "Chestnut GPU unavailable. PCIe link is not up. Check the GPU is securely seated."
alerts = (
("Offroad_ChestnutNotDetected", missing, None),
("Offroad_ChestnutUpdateFailed", update_failed, None),
("Offroad_ChestnutUncompiled", uncompiled, None),
("Offroad_ChestnutPcieUnavailable", self.pcie_failed, pcie_alert),
("Offroad_ChestnutOverheated", self.overheated, f"{state.tempC:.0f} °C" if state is not None else None),
("Offroad_ChestnutUsbSlow", slow_usb, f"{devices[0]['speedMbps']} Mbps" if slow_usb else None),
)
active_alert = next((name for name, active, _ in alerts if active), None)
set_alert("Offroad_ChestnutBranch", not release and len(devices) == 1 and not missing)
for name, _, extra_text in alerts:
set_alert(name, name == active_alert, extra_text)
set_alert("Offroad_ChestnutPcieUnavailable", self.pcie_failed, pcie_alert)
set_alert("Offroad_ChestnutUncompiled", offroad and firmware_ok and not chestnut_compiled())
set_alert("Offroad_ChestnutUpdateFailed", offroad and firmware_failed)
self.offroad = offroad