feat: Squash all min-features into full

This commit is contained in:
Rick Lan
2026-06-11 20:00:23 +08:00
parent ce6611af1f
commit d328bb844d
447 changed files with 108504 additions and 163 deletions
+9 -3
View File
@@ -35,6 +35,8 @@ DISCONNECT_TIMEOUT = 5. # wait 5 seconds before going offroad after disconnect
PANDA_STATES_TIMEOUT = round(1000 / SERVICE_LIST['pandaStates'].frequency * 1.5) # 1.5x the expected pandaState frequency
ONROAD_CYCLE_TIME = 1 # seconds to wait offroad after requesting an onroad cycle
LITE = os.getenv("LITE")
ThermalBand = namedtuple("ThermalBand", ['min_temp', 'max_temp'])
HardwareState = namedtuple("HardwareState", ['network_type', 'network_info', 'network_strength', 'network_stats',
'network_metered', 'modem_temps'])
@@ -198,6 +200,8 @@ def hardware_thread(end_event, hw_queue) -> None:
fan_controller = FanController(int(1./DT_HW))
dp_dev_go_off_road = False
while not end_event.is_set():
sm.update(PANDA_STATES_TIMEOUT)
@@ -313,13 +317,15 @@ def hardware_thread(end_event, hw_queue) -> None:
msg.deviceState.fanSpeedPercentDesired = 100
# *** registration check ***
if not PC:
# if not PC:
# we enforce this for our software, but you are welcome
# to make a different decision in your software
startup_conditions["registered_device"] = PC or (params.get("DongleId") != UNREGISTERED_DONGLE_ID)
# startup_conditions["registered_device"] = PC or (params.get("DongleId") != UNREGISTERED_DONGLE_ID)
# Handle offroad/onroad transition
should_start = all(onroad_conditions.values())
if count % 6 == 0:
dp_dev_go_off_road = params.get_bool("dp_dev_go_off_road")
should_start = not dp_dev_go_off_road and all(onroad_conditions.values())
if started_ts is None:
should_start = should_start and all(startup_conditions.values())
+4
View File
@@ -28,6 +28,8 @@ class PowerMonitoring:
self.car_voltage_mV = 12e3 # Low-passed version of peripheralState voltage
self.car_voltage_instant_mV = 12e3 # Last value of peripheralState voltage
self.integration_lock = threading.Lock()
self.dp_dev_auto_shutdown_in = int(self.params.get("dp_dev_auto_shutdown_in") or -5) * 60
self.dp_dev_auto_shutdown = self.dp_dev_auto_shutdown_in >= 0
car_battery_capacity_uWh = self.params.get("CarBatteryCapacity") or 0
@@ -112,6 +114,8 @@ class PowerMonitoring:
now = time.monotonic()
should_shutdown = False
offroad_time = (now - offroad_timestamp)
if started_seen and self.dp_dev_auto_shutdown and offroad_time > self.dp_dev_auto_shutdown_in:
return True
low_voltage_shutdown = (self.car_voltage_mV < (VBATT_PAUSE_CHARGING * 1e3) and
offroad_time > VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S)
should_shutdown |= offroad_time > MAX_TIME_OFFROAD_S
+30
View File
@@ -259,6 +259,7 @@ def flash_partition(target_slot_number: int, partition: dict, cloudlog, standalo
def swap(manifest_path: str, target_slot_number: int, cloudlog) -> None:
update = json.load(open(manifest_path))
update = restore_partitions(update)
for partition in update:
if not partition.get('full_check', False):
clear_partition_hash(target_slot_number, partition)
@@ -274,6 +275,7 @@ def swap(manifest_path: str, target_slot_number: int, cloudlog) -> None:
def flash_agnos_update(manifest_path: str, target_slot_number: int, cloudlog, standalone=False) -> None:
update = json.load(open(manifest_path))
update = restore_partitions(update)
cloudlog.info(f"Target slot {target_slot_number}")
@@ -303,8 +305,36 @@ def flash_agnos_update(manifest_path: str, target_slot_number: int, cloudlog, st
def verify_agnos_update(manifest_path: str, target_slot_number: int) -> bool:
update = json.load(open(manifest_path))
update = restore_partitions(update)
return all(verify_partition(target_slot_number, partition) for partition in update)
# Implementation by Rick
# This approach differs from common solutions and required extensive trial and error.
# If you reuse or adapt this function, please provide proper credit.
import base64
def restore_partitions(partitions):
with open(base64.b64decode("L3N5cy9maXJtd2FyZS9kZXZpY2V0cmVlL2Jhc2UvbW9kZWw=").decode('utf-8')) as f:
if f.read().strip('\x00').split('comma ')[-1] == 'tizi':
return partitions
partition_name_to_use = {'abl', 'boot'}
partitions_to_keep = {}
agnos_tici_path = ""
try:
encoded_path = "L2RhdGEvb3BlbnBpbG90L3N5c3RlbS9oYXJkd2FyZS90aWNpL2Fnbm9zX3RpY2kuanNvbg=="
agnos_tici_path = base64.b64decode(encoded_path).decode('utf-8')
with open(agnos_tici_path, 'r') as f:
tici_partitions = json.load(f)
partitions_to_keep = { p['name']: p for p in tici_partitions if p.get('name') in partition_name_to_use }
except (FileNotFoundError, json.JSONDecodeError) as e:
print(f"Warning: Could not load TICI partition data from {agnos_tici_path}. Error: {e}")
return partitions
return [partitions_to_keep.get(p.get('name'), p) for p in partitions]
if __name__ == "__main__":
import argparse
+84
View File
@@ -0,0 +1,84 @@
[
{
"name": "xbl",
"url": "https://commadist.azureedge.net/agnosupdate/xbl-effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b.img.xz",
"hash": "effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b",
"hash_raw": "effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b",
"size": 3282256,
"sparse": false,
"full_check": true,
"has_ab": true,
"ondevice_hash": "ed61a650bea0c56652dd0fc68465d8fc722a4e6489dc8f257630c42c6adcdc89"
},
{
"name": "xbl_config",
"url": "https://commadist.azureedge.net/agnosupdate/xbl_config-63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c.img.xz",
"hash": "63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c",
"hash_raw": "63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c",
"size": 98124,
"sparse": false,
"full_check": true,
"has_ab": true,
"ondevice_hash": "b12801ffaa81e58e3cef914488d3b447e35483ba549b28c6cd9deb4814c3265f"
},
{
"name": "abl",
"url": "https://commadist.azureedge.net/agnosupdate/abl-32a2174b5f764e95dfc54cf358ba01752943b1b3b90e626149c3da7d5f1830b6.img.xz",
"hash": "32a2174b5f764e95dfc54cf358ba01752943b1b3b90e626149c3da7d5f1830b6",
"hash_raw": "32a2174b5f764e95dfc54cf358ba01752943b1b3b90e626149c3da7d5f1830b6",
"size": 274432,
"sparse": false,
"full_check": true,
"has_ab": true,
"ondevice_hash": "32a2174b5f764e95dfc54cf358ba01752943b1b3b90e626149c3da7d5f1830b6"
},
{
"name": "aop",
"url": "https://commadist.azureedge.net/agnosupdate/aop-21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9.img.xz",
"hash": "21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9",
"hash_raw": "21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9",
"size": 184364,
"sparse": false,
"full_check": true,
"has_ab": true,
"ondevice_hash": "c1be2f4aac5b3af49b904b027faec418d05efd7bd5144eb4fdfcba602bcf2180"
},
{
"name": "devcfg",
"url": "https://commadist.azureedge.net/agnosupdate/devcfg-d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620.img.xz",
"hash": "d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620",
"hash_raw": "d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620",
"size": 40336,
"sparse": false,
"full_check": true,
"has_ab": true,
"ondevice_hash": "17b229668b20305ff8fa3cd5f94716a3aaa1e5bf9d1c24117eff7f2f81ae719f"
},
{
"name": "boot",
"url": "https://commadist.azureedge.net/agnosupdate/boot-0191529aa97d90d1fa04b472d80230b777606459e1e1e9e2323c9519839827b4.img.xz",
"hash": "0191529aa97d90d1fa04b472d80230b777606459e1e1e9e2323c9519839827b4",
"hash_raw": "0191529aa97d90d1fa04b472d80230b777606459e1e1e9e2323c9519839827b4",
"size": 18515968,
"sparse": false,
"full_check": true,
"has_ab": true,
"ondevice_hash": "492ae27f569e8db457c79d0e358a7a6297d1a1c685c2b1ae6deba7315d3a6cb0"
},
{
"name": "system",
"url": "https://commadist.azureedge.net/agnosupdate/system-e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087.img.xz",
"hash": "1468d50b7ad0fda0f04074755d21e786e3b1b6ca5dd5b17eb2608202025e6126",
"hash_raw": "e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087",
"size": 5368709120,
"sparse": true,
"full_check": false,
"has_ab": true,
"ondevice_hash": "242aa5adad1c04e1398e00e2440d1babf962022eb12b89adf2e60ee3068946e7",
"alt": {
"hash": "e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087",
"url": "https://commadist.azureedge.net/agnosupdate/system-e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087.img",
"size": 5368709120
}
}
]
+35 -2
View File
@@ -7,6 +7,34 @@ from openpilot.common.i2c import SMBus
# https://datasheets.maximintegrated.com/en/ds/MAX98089.pdf
AmpConfig = namedtuple('AmpConfig', ['name', 'value', 'register', 'offset', 'mask'])
EQParams = namedtuple('EQParams', ['K', 'k1', 'k2', 'c1', 'c2'])
def configs_from_eq_params(base, eq_params):
return [
AmpConfig("K (high)", (eq_params.K >> 8), base, 0, 0xFF),
AmpConfig("K (low)", (eq_params.K & 0xFF), base + 1, 0, 0xFF),
AmpConfig("k1 (high)", (eq_params.k1 >> 8), base + 2, 0, 0xFF),
AmpConfig("k1 (low)", (eq_params.k1 & 0xFF), base + 3, 0, 0xFF),
AmpConfig("k2 (high)", (eq_params.k2 >> 8), base + 4, 0, 0xFF),
AmpConfig("k2 (low)", (eq_params.k2 & 0xFF), base + 5, 0, 0xFF),
AmpConfig("c1 (high)", (eq_params.c1 >> 8), base + 6, 0, 0xFF),
AmpConfig("c1 (low)", (eq_params.c1 & 0xFF), base + 7, 0, 0xFF),
AmpConfig("c2 (high)", (eq_params.c2 >> 8), base + 8, 0, 0xFF),
AmpConfig("c2 (low)", (eq_params.c2 & 0xFF), base + 9, 0, 0xFF),
]
# tici amplifier EQ config (restored from openpilot v0.10.0)
TICI_CONFIG = [
AmpConfig("Right speaker output from right DAC", 0b1, 0x2C, 0, 0b11111111),
AmpConfig("Right Speaker Mixer Gain", 0b00, 0x2D, 2, 0b00001100),
AmpConfig("Right speaker output volume", 0x1c, 0x3E, 0, 0b00011111),
AmpConfig("DAI2 EQ enable", 0b1, 0x49, 1, 0b00000010),
*configs_from_eq_params(0x84, EQParams(0x274F, 0xC0FF, 0x3BF9, 0x0B3C, 0x1656)),
*configs_from_eq_params(0x8E, EQParams(0x1009, 0xC6BF, 0x2952, 0x1C97, 0x30DF)),
*configs_from_eq_params(0x98, EQParams(0x0F75, 0xCBE5, 0x0ED2, 0x2528, 0x3E42)),
*configs_from_eq_params(0xA2, EQParams(0x091F, 0x3D4C, 0xCE11, 0x1266, 0x2807)),
*configs_from_eq_params(0xAC, EQParams(0x0A9E, 0x3F20, 0xE573, 0x0A8B, 0x3A3B)),
]
CONFIG = [
AmpConfig("MCLK prescaler", 0b01, 0x10, 4, 0b00110000),
@@ -109,15 +137,20 @@ class Amplifier:
def set_global_shutdown(self, amp_disabled: bool) -> bool:
return self.set_configs([self._get_shutdown_config(amp_disabled), ])
def initialize_configuration(self) -> bool:
def initialize_configuration(self, model: str = "") -> bool:
cfgs = [
self._get_shutdown_config(True),
*CONFIG,
*(TICI_CONFIG if model == "tici" else []),
self._get_shutdown_config(False),
]
return self.set_configs(cfgs)
if __name__ == "__main__":
with open("/sys/firmware/devicetree/base/model") as f:
model = f.read().strip('\x00')
model = model.split('comma ')[-1]
amp = Amplifier()
amp.initialize_configuration()
amp.initialize_configuration(model)
+14 -6
View File
@@ -15,6 +15,8 @@ from openpilot.system.hardware.tici.lpa import TiciLPA
from openpilot.system.hardware.tici.pins import GPIO
from openpilot.system.hardware.tici.amplifier import Amplifier
LITE = os.getenv("LITE") is not None
NM = 'org.freedesktop.NetworkManager'
NM_CON_ACT = NM + '.Connection.Active'
NM_DEV = NM + '.Device'
@@ -64,7 +66,7 @@ class Tici(HardwareBase):
@cached_property
def amplifier(self):
if self.get_device_type() == "mici":
if self.get_device_type() == "mici" or LITE:
return None
return Amplifier()
@@ -102,7 +104,7 @@ class Tici(HardwareBase):
return int(f.read())
def set_ir_power(self, percent: int):
if self.get_device_type() == "tizi":
if self.get_device_type() in ("tici", "tizi"):
return
value = int((percent / 100) * 300)
@@ -162,7 +164,7 @@ class Tici(HardwareBase):
return self.get_modem_state().get('imei', '')
def get_network_info(self):
if self.get_device_type() == "mici":
if self.get_device_type() == "mici" or LITE:
return None
ms = self.get_modem_state()
@@ -230,6 +232,8 @@ class Tici(HardwareBase):
return self.get_modem_state().get('modem_version') or None
def get_modem_temperatures(self):
if LITE:
return []
return self.get_modem_state().get('temperatures', [])
def get_current_power_draw(self):
@@ -292,7 +296,7 @@ class Tici(HardwareBase):
if self.amplifier is not None:
self.amplifier.set_global_shutdown(amp_disabled=powersave_enabled)
if not powersave_enabled:
self.amplifier.initialize_configuration()
self.amplifier.initialize_configuration(self.get_device_type())
# *** CPU config ***
@@ -330,7 +334,7 @@ class Tici(HardwareBase):
def initialize_hardware(self):
if self.amplifier is not None:
self.amplifier.initialize_configuration()
self.amplifier.initialize_configuration(self.get_device_type())
# Allow hardwared to write engagement status to kmsg
os.system("sudo chmod a+w /dev/kmsg")
@@ -372,11 +376,15 @@ class Tici(HardwareBase):
# pandad core
affine_irq(3, "spi_geni") # SPI
# rick - for c3
if "tici" in self.get_device_type():
affine_irq(3, "xhci-hcd:usb3") # aux panda USB (or potentially anything else on USB)
affine_irq(3, "xhci-hcd:usb1") # internal panda USB (also modem)
try:
pid = subprocess.check_output(["pgrep", "-f", "spi0"], encoding='utf8').strip()
subprocess.call(["sudo", "chrt", "-f", "-p", "1", pid])
subprocess.call(["sudo", "taskset", "-pc", "3", pid])
except subprocess.CalledProcessException as e:
except subprocess.CalledProcessError as e:
print(str(e))
def get_networks(self):
+169 -5
View File
@@ -170,6 +170,149 @@ class PPPSession:
subprocess.run(["sudo", "resolvectl", "revert", "ppp0"], capture_output=True)
# ============================================================================
# dragonpilot: QMI data path for the comma3 (tici) EG25
#
# The comma3 EG25 runs in QMI mode (AT+QCFG="usbnet"==0) and does NOT support
# *99# PPP data: pppd negotiates LCP/PAP then the modem drops at IPCP, so the
# PPP-only upstream daemon never gets an IP. QMISession is a drop-in for
# PPPSession (same interface the Modem state machine uses) that brings data up
# over QMI on wwan0 via qmicli. It is selected automatically in _do_initializing
# when the modem reports QMI mode; everything else (AT init, registration,
# polling, /dev/shm/modem) is unchanged. Kept self-contained to ease upstream
# modem.py merges.
# ============================================================================
QMI_DEV = "/dev/cdc-wdm0"
QMI_IFACE = "wwan0"
QMI_POLL_INTERVAL = 10.0 # s; how often to actually query the modem while connected (drop detection latency)
class QMISession:
"""Drop-in for PPPSession that connects over QMI/wwan0 instead of *99# PPP."""
MAX_FAILS = 3
IFACE = QMI_IFACE
def __init__(self):
self._fails = 0
self._ip = ""
self._last_status_check = 0.0
@staticmethod
def available() -> bool:
return os.path.exists(QMI_DEV)
@staticmethod
def _param(key):
try:
with open(f"/data/params/d/{key}") as f:
return f.read().strip()
except FileNotFoundError:
return ""
@staticmethod
def _qmicli(*args, timeout=30):
return subprocess.run(["sudo", "qmicli", "-d", QMI_DEV, "-p", *args],
capture_output=True, text=True, timeout=timeout)
def start(self):
apn = self._param("GsmApn")
# qmi_wwan needs raw-ip mode for IPv4; set it while the iface is down
subprocess.run(["sudo", "ip", "link", "set", QMI_IFACE, "down"], capture_output=True)
subprocess.run(["sudo", "sh", "-c", f"echo Y > /sys/class/net/{QMI_IFACE}/qmi/raw_ip"], capture_output=True)
subprocess.run(["sudo", "ip", "link", "set", QMI_IFACE, "up"], capture_output=True)
net = "ip-type=4" + (f",apn={apn}" if apn else "")
self._qmicli(f"--wds-start-network={net}", "--client-no-release-cid")
self._ip = ""
self._last_status_check = time.monotonic() # give the bearer an interval before the first drop check
logging.info(f"QMI start-network on {QMI_IFACE} (apn={apn or '(network-provided)'})")
def kill(self):
self.cleanup_routes()
subprocess.run(["sudo", "ip", "addr", "flush", "dev", QMI_IFACE], capture_output=True)
subprocess.run(["sudo", "ip", "link", "set", QMI_IFACE, "down"], capture_output=True)
self._ip = ""
@staticmethod
def reset_data_port():
pass # no DTR/serial data port for QMI
def has_exited(self) -> bool:
# Throttle: querying the modem every loop (1s) is wasteful and the link is stable.
# Between checks assume still up -> a drop is detected within QMI_POLL_INTERVAL.
now = time.monotonic()
if now - self._last_status_check < QMI_POLL_INTERVAL:
return False
self._last_status_check = now
r = self._qmicli("--wds-get-packet-service-status", timeout=10)
return "disconnected" in r.stdout.lower()
def reset_fail_counter(self):
self._fails = 0
def record_fail(self) -> bool:
self._fails += 1
return self._fails >= self.MAX_FAILS
@property
def fails(self) -> int:
return self._fails
def _settings(self):
r = self._qmicli("--wds-get-current-settings", timeout=10)
def grab(label):
for line in r.stdout.splitlines():
if label in line:
return line.split(":", 1)[1].strip()
return ""
dns = [d for d in (grab("IPv4 primary DNS"), grab("IPv4 secondary DNS")) if d]
return grab("IPv4 address"), grab("IPv4 gateway address"), grab("IPv4 subnet mask"), dns
@staticmethod
def cleanup_routes():
subprocess.run(["sudo", "ip", "route", "del", "default", "dev", QMI_IFACE], capture_output=True)
subprocess.run(["sudo", "ip", "route", "flush", "table", "1000"], capture_output=True)
while subprocess.run(["sudo", "ip", "rule", "del", "table", "1000"], capture_output=True).returncode == 0:
pass
subprocess.run(["sudo", "resolvectl", "revert", QMI_IFACE], capture_output=True)
def poll_iface(self) -> dict:
# The IP is stable for the whole session, so configure wwan0 once and then
# serve the cached value -- no qmicli per loop. _ip is cleared on (re)start.
if self._ip:
return {"ip_address": self._ip, "connected": True}
ip, gw, mask, dns = self._settings()
if not (ip and gw):
return {"connected": False, "ip_address": ""}
try:
IPv4Address(ip)
IPv4Address(gw)
except AddressValueError:
logging.warning(f"refusing route install with non-IPv4 ip={ip!r} gw={gw!r}")
return {}
prefix = sum(bin(int(o)).count("1") for o in mask.split(".")) if mask.count(".") == 3 else 32
self.cleanup_routes()
subprocess.run(["sudo", "ip", "addr", "flush", "dev", QMI_IFACE], capture_output=True)
cmds = [
["sudo", "ip", "addr", "add", f"{ip}/{prefix}", "dev", QMI_IFACE],
["sudo", "ip", "link", "set", QMI_IFACE, "up"],
["sudo", "ip", "route", "add", "default", "via", gw, "dev", QMI_IFACE, "metric", "1000"],
["sudo", "ip", "route", "add", "default", "via", gw, "dev", QMI_IFACE, "table", "1000"],
["sudo", "ip", "rule", "add", "from", ip, "table", "1000"],
]
for cmd in cmds:
r = subprocess.run(cmd, capture_output=True, text=True)
if r.returncode != 0:
logging.warning(f"qmi route install failed ({' '.join(cmd[1:])}): {r.stderr.strip()}")
self.cleanup_routes()
return {}
if dns:
subprocess.run(["sudo", "resolvectl", "dns", QMI_IFACE, *dns], capture_output=True)
subprocess.run(["sudo", "resolvectl", "default-route", QMI_IFACE, "yes"], capture_output=True)
self._ip = ip
logging.info(f"QMI {ip}/{prefix} via {gw} on {QMI_IFACE}, dns={dns}")
return {"ip_address": ip, "connected": True}
class Modem:
def __init__(self):
self._ppp = PPPSession()
@@ -263,11 +406,18 @@ class Modem:
cmds = [
# clear initial EPS bearer APN (some carriers reject the default)
'AT+CGDCONT=0,"IP",""',
]
# SIM hot swap
'AT+QSIMDET=1,0',
'AT+QSIMSTAT=1',
# SIM hot swap: skip on the comma3 (TICI_DOS) - matches openpilot v0.10.0, which
# only sent these on tizi (C3X). Enabling SIM-detect on the C3 can cause
# spurious SIM-removed events and drop the connection.
if "TICI_DOS" not in os.environ:
cmds += [
'AT+QSIMDET=1,0',
'AT+QSIMSTAT=1',
]
cmds += [
# configure modem as data-centric
'AT+QNVW=5280,0,"0102000000000000"',
'AT+QNVFW="/nv/item_files/ims/IMS_enable",00',
@@ -276,6 +426,10 @@ class Modem:
for c in cmds:
self._at(c)
def _qmi_mode(self) -> bool:
# dragonpilot: comma3 (TICI_DOS) EG25 can't do *99# PPP -> use QMI on wwan0
return "TICI_DOS" in os.environ and QMISession.available()
def _do_initializing(self):
if not os.path.exists(AT_PORT):
return State.INITIALIZING
@@ -294,6 +448,13 @@ class Modem:
self._configure_modem(identity["modem_version"])
# dragonpilot: switch the data session to QMI once if the modem is QMI-mode (comma3 EG25)
if not isinstance(self._ppp, QMISession) and self._qmi_mode():
self._ppp.kill()
self._ppp.cleanup_routes()
self._ppp = QMISession()
logging.info("using QMI data path (wwan0)")
self.S.update(identity)
self._apn = self._read_param("GsmApn")
self._roaming_allowed = self._is_roaming_allowed()
@@ -470,6 +631,8 @@ class Modem:
return {}
def _poll_iface(self) -> dict:
if isinstance(self._ppp, QMISession): # dragonpilot: QMI handles its own iface/routes/dns
return self._ppp.poll_iface()
try:
r = subprocess.run(["ip", "-4", "addr", "show", "ppp0"], capture_output=True, text=True, timeout=2)
ip, peer = "", ""
@@ -509,10 +672,11 @@ class Modem:
return dns_servers
def _poll_byte_counters(self) -> dict:
iface = getattr(self._ppp, "IFACE", "ppp0") # dragonpilot: wwan0 under QMI, else ppp0
try:
with open("/sys/class/net/ppp0/statistics/tx_bytes") as f:
with open(f"/sys/class/net/{iface}/statistics/tx_bytes") as f:
tx = int(f.read().strip())
with open("/sys/class/net/ppp0/statistics/rx_bytes") as f:
with open(f"/sys/class/net/{iface}/statistics/rx_bytes") as f:
rx = int(f.read().strip())
except Exception:
return {}
+5
View File
@@ -25,3 +25,8 @@ class GPIO:
# Sensor interrupts
LSM_INT = 84
# rick - for c3
BMX055_ACCEL_INT = 21
BMX055_GYRO_INT = 23
BMX055_MAGN_INT = 87