Документацію написано англійською.
Cellular
Where the modem stands (October 2026). The detailed, step-by-step diary in Russian is
docs/ru/cellular.md.
What works
The modem (the Hexagon MPSS) boots from XNU: its firmware goes through TrustZone, the shared memory, SMP2P, GLINK and QRTR come up, and it announces about 88 QMI services.
Its file systems (modemst1, modemst2, fsc, fsg) are served from the UFS partitions over RMTFS, read-only: writes stay in kernel memory for the session, the phone's flash is never touched.
TFTP over QRTR and the service registry (what Android's tqftpserv and pd-mapper do) are ours.
The IPA packet engine runs: firmware through TrustZone, its clock through RPMh, both of its SMMU streams through a translation context of ours (the hypervisor takes one match register per stream), the registers Android programs, the modem's filter-table item in shared memory, and the SMP2P entry
ipathe modem's driver waits for. The INIT_DRIVER handshake goes through.The modem goes online and stays up. The SIM is found (physical slot 2 on the first phone), logical slot 1 is pointed at it, the provisioning session is bound to its USIM, and NAS reads the signal (-70 to -87 dBm on UMTS).
SpringBoard's status bar: the signal item is hooked (
userland/src/inject_sb.c) and shows the kernel's bars (sysctl kern.hakios.cellular_bars) once the modem is registered.The data session: once NAS reports registration, the kernel binds its QMI WDS client to QMAP mux 1 of the embedded port (the IPA), sets the port's data format (WDA: raw IP in QMAP, no checksum offload), starts an IPv4 session on the SIM's default profile and reads its settings (address, gateway, DNS, MTU:
sysctl kern.hakios.cellular_ip,_gateway,_dns,_mtu,_data).
The data path (2.0)
Packets go through the IPA, the SoC's packet engine, the way Linux's drivers/net/ipa does it
for IPA 4.11; everything is in drivers/modem/hakios.ipa.c and hakios.cell.c.
- Before the modem starts, the kernel sets up this processor's side of the IPA: four GSI channels with their event rings (the command channel 5, the LAN receive channel 14, the modem transmit channel 2 and the modem receive channel 7), the matching endpoints (7, 9, 2, 16: QMAP headers, deaggregation on the way out, aggregation up to 12 KB or 500 us on the way in), and the IPA's filter, route and header tables through immediate commands, so that the modem finds valid tables and adds its own rules to them.
- The rings are polled by the interface's thread; no interrupt is taken from the GSI.
- The modem's packets are an Ethernet-looking interface,
en1, with a made-up router behind it. The kernel answers ARP for every address and DHCP with the session's address, a /24 around it, the router and the DNS servers, so iOS's configd configuresen1as it would a USB Ethernet adapter and routes through it. IPv6 is not carried. - The modem is told about this side the way ModemManager does for a phone behind the IPA
(
net_driver == "ipa"): DPM Open Port (Data Port Mapper) names the embedded port's IPA endpoints (the modem receives on our TX endpoint 2 and sends on our RX endpoint 16), then WDA Set Data Format sets raw IP in QMAP on it. Only then do the WDS steps run: Bind Mux Data Port (mux 1), Set IP Family (IPv4), Start Network Interface (with the APN) once NAS is registered, retried while the packet domain is still attaching. Skipping the DPM step was why WDA and the session answered InvalidOperation and the bind Internal (cycles 38-41): the modem had no embedded port to bind to. - The rings are polled by the interface's thread; no interrupt is taken from the GSI.
- The modem's packets are an Ethernet-looking interface,
en1, with a made-up router behind it. The kernel answers ARP for every address and DHCP with the session's address, a /24 around it, the router and the DNS servers, so iOS's configd configuresen1as it would a USB Ethernet adapter and routes through it. IPv6 is not carried. modem_ipa_data_off=1leaves all of this out;modem_dpm_off=1/modem_wda_off=1skip those steps;modem_ipa_data_trace=1logs every packet.
Another phone: the SoC addresses
The modem and IPA drivers carry the POCO X5 Pro's register and memory addresses as defaults and
take any that differ from boot-args soc_* (hakios.mpss.c soc_override(), hakios.ipa.c).
tools/soc-args.py reads a phone's own device tree (devices/<name>/pull-soc.sh pulls
/sys/firmware/fdt over adb) and prints the soc_* it needs; the run scripts append
devices/<name>/soc.args. For the first phone every generated value equals the default, so it
needs no arguments.
Not yet
- Registration reaches full LTE service, but the data session is still refused until the packet domain attaches; the DPM/WDA/bind order above is the current attempt, untested on hardware since the phone went off USB.
- Calls, SMS, IPv6.
How to run it
XL_SB_DELAY=8 tools/modem-cycle.sh "modem=1 modem_again=1 modem_buffer=0xa8000000" 600
modem_step=N runs the bring-up step by step and returns to the loader with the log;
modem=1 brings the modem up beside the system. Everything is in
drivers/modem/hakios.mpss.c (the modem), hakios.nas.c (NAS), hakios.qmi.c (QMI).
Kernel arguments
| Argument | Meaning |
|---|---|
modem=1 |
the modem beside the system, on a thread of its own |
modem_step=N |
steps 1..N, then back to the loader (modem_return=1) |
modem_again=1 |
start it although a kernel before this one left it shut down |
modem_buffer=0xa8000000 |
the RMTFS buffer (below 4 GB) |
modem_nas_wait=S |
how long step 11 waits for registration |
modem_online=0 |
do not put the modem online |
modem_ipa=3 (default) |
the SMP2P entry ipa: valid, clock on |
modem_sim_switch_off=1 |
do not point logical slot 1 at the slot with the ready card |
modem_sim_probe_off=1 |
no SIM and network-selection queries |
modem_data_off=1 |
no data session at all |
modem_dpm_off=1 / modem_wda_off=1 |
skip the DPM / WDA step |
modem_data_nobind=1 |
the session without binding the client to the mux |
modem_apn=NAME |
the session to that APN instead of the SIM's default profile |
modem_ipa_data_off=1 |
no IPA data path for this processor, no en1 |
modem_ipa_data_trace=1 |
a log line per packet, command and DHCP/ARP answer |