MT-SMS (incoming SMS) and VoLTE work on Android 14 but not Ubuntu — rild does not register IMS

Incoming SMS (MT-SMS) and VoLTE do not work on Ubuntu, but work on Android 14 — same hardware/SIM (IMS not brought up by particle-tachyon-rild)

Summary: On the same Tachyon, same eSIM, same carrier, incoming SMS and voice calls work on Android 14 but NOT on Ubuntu 20.04 (headless). Root cause: on Ubuntu, particle-tachyon-rild never brings up the IMS PDN / registers IMS, so mobile-terminated SMS (delivered over IMS on this network) never arrive. Outgoing SMS (MO) and data work fine on both. This looks like a missing IMS/VoLTE capability in the Ubuntu cellular stack.

Hardware / setup

  • Particle Tachyon, modem Quectel SG560D, baseband BP01.001 (SG560DWFPAR01A01)
  • Carrier: Lyca Mobile Poland (MVNO on Plus / Polkomtel, MCC/MNC 260/01), eSIM
  • Network supports VoLTE: LteVopsSupportInfo: mVopsSupport = 2 (VoPS supported)

Symptom

  • Ubuntu 20.04: sending SMS works; receiving SMS never works — incoming messages never reach modem memory (AT+CPMS SM/ME stay 0) on LTE or 2G; voice not possible. Tested exhaustively (re-attach, reboot, modem power-cycle, factory reset, CGSMS 0/1, NV writes).
  • Android 14 (beta): receiving SMS works (a full day's backlog of queued MT-SMS flushed immediately on first registration), and voice calls work too.

Root cause (from Android dumpsys telephony.registry)

On Android the stack brings up a dedicated IMS PDN and registers IMS:

  • Separate APN ims (IPV4V6) on rmnet_data1, in addition to the data APN
  • Network-assigned P-CSCF addresses: 2a01:2e0:1e:f060::4, 2a01:2e0:1e:f040::4
  • AP-side IMS enablement: persist.dbg.volte_avail_ovr = 1, Qualcomm com.qti.phone service running
  • CS domain reports availableServices=[VOICE, SMS, VIDEO]

On Ubuntu, particle-tachyon-rild:

  • Only brings up the configured data APN; never establishes the ims PDN, so no P-CSCF, no IMS registration → MT-SMS (delivered over IMS on this network) is never received.
  • AT+QCFG="ims", AT+QIMSCFG, AT+QNWPREFCFG are blocked (return ERROR) via sendat, and writing /nv/item_files/ims/IMS_enable=01 has no effect (rild does not trigger IMS registration).

Request

Please add IMS / VoLTE support to the Ubuntu cellular stack (particle-tachyon-rild) — i.e. bring up the ims APN/PDN, obtain P-CSCF, and perform IMS registration (as Android's com.qti.phone does) — so that incoming SMS (MT-SMS) and VoLTE voice work on Ubuntu. Alternatively, expose the modem's IMS controls (e.g. allow AT+QCFG="ims" / the relevant QMI IMS services) so users can enable it themselves.

This is on a network where MT-SMS is delivered over IMS; without IMS registration the device cannot receive SMS, which breaks any 2FA / inbound-SMS use case on Ubuntu. Other users have asked for VoLTE on Tachyon as well.

Update — confirmed working on Ubuntu 24.04 (plus a /persist mount fix that may help others)

Follow-up to my own report. I reflashed the same Tachyon (same eSIM, same Lyca / Plus-Polkomtel network) to Ubuntu 24.04, and incoming SMS (MT-SMS) now works out of the box — the whole backlog of queued messages flushed in on first registration, and voice works too. So this confirms the blocker is the 20.04 cellular stack, not the SIM/carrier or the hardware.

24.04 uses ModemManager (~1.22+) with libqmi ≥ 1.32 (QRTR support), which can talk to the integrated QRTR modem and bring up the IMS bearer. Stock 20.04 ships libqmi 1.30.4 without QRTR, and particle-tachyon-rild never registers IMS — so on networks that deliver MT-SMS over IMS (Plus/Polkomtel here, mVopsSupport=2), incoming SMS never arrive.

The gotcha on a fresh 24.04 flash (the bit that may save someone time):
On first boot particle-modem-setup.service failed and the modem never appeared:

mkdir: cannot create directory '/persist': No such device

The persist partition had no filesystem (blkid showed no TYPE; mounting gave "bad superblock"). Creating a filesystem on it and re-running the setup service fixed it and the modem came up under ModemManager:

# 1) Identify the persist partition FIRST — do NOT guess the device node.
lsblk -o NAME,PARTLABEL,FSTYPE,SIZE        # look for PARTLABEL "persist" with empty FSTYPE
# 2) ONLY if it is genuinely blank (no FSTYPE) — this is DESTRUCTIVE, be sure:
sudo mkfs.ext4 -F -L persist /dev/<persist-partition>
# 3) Let the setup service mount it and bring up the modem:
sudo systemctl restart particle-modem-setup

After that ModemManager detects the modem, registers on the network, and SMS receive (and voice) work. :warning: mkfs is destructive — confirm the partition truly has no FSTYPE before running it; if yours already has a filesystem, your issue is something else.

For 20.04 specifically: I also built the full modern stack from source (libqrtr-glib 1.2.0 + libqmi 1.34 + libmbim 1.30 + ModemManager 1.22) and got MM to detect, group the net ports, and register the SG560D over QRTR. But MM still can't bring up the data/IMS bearer because the 20.04 image uses Qualcomm's downstream rmnet/IPA driver (no manageable …/device/driver), so QMIWDS_ADMIN_SET_DATA_FORMAT fails and no IMS PDN comes up. That looks like a kernel-level gap (upstream CONFIG_QCOM_IPA / CONFIG_RMNET), which the 24.04 image already handles. So userspace is solvable; the 20.04 data/IMS path needs the kernel side.

TL;DR: use 24.04 for working MT-SMS/voice today (mind the /persist fix above); on 20.04 the missing piece is the rmnet/IPA kernel driver + IMS in particle-tachyon-rild. Happy to share more detail if useful.

FWIW, there is ongoing work in libqmi and ModemManager to enable VoLTE with Qualcomm-based devices via NLnet; OpenIMSd

Thanks @dcbw — that's exactly the missing piece, and great to hear it's being worked on upstream.

One data point that might be useful: on this same Tachyon (Quectel SG560D / QCM6490), incoming MT-SMS already works out-of-the-box on Ubuntu 24.04 — ModemManager (~1.22) + libqmi with QRTR brings the modem up, it registers on LTE, and SMS-over-IMS messages arrive (a self-test round-trips in ~10s, and a queued backlog flushed immediately on first registration). So it looks like the modem's internal IMS client handles the SMS-over-IMS path on its own once MM attaches it — no separate IMS daemon involved.

What's still missing here is VoLTE voice, which is presumably exactly what OpenIMSd + the libqmi/MM work targets (bringing up the IMS PDN, P-CSCF, registration and call setup).

Question: is OpenIMSd needed for IMS-delivered SMS too, or only for voice/video? On a network that delivers MT-SMS over IMS I'd have expected SMS to also depend on IMS registration — yet 24.04 already receives them without any extra daemon, so I'm curious where the line actually is (modem-internal IMS for messaging vs. host-driven IMS for voice).

Either way — if I can be useful, I'd be glad to help: testing on the SG560D / QCM6490 (a slightly different target than the PinePhone), implementation, or beta testing. I can provide QMI traces, dumps, logs, whatever's helpful. Just let me know.

Spent the weekend going a lot deeper on this, since the hardware was just sitting on my bench. TL;DR: I flashed the stock Android 14 image back, captured a live (answered) VoLTE call, and reverse-engineered the audio + signalling path end to end. Sharing the concrete bits in case they're useful for the OpenIMSd / AudioReach voice effort — happy to hand over the full writeup.

Signalling is the modem's job here — and it works. SIP, RTP (EVS + AMR), P-CSCF discovery and the MMTEL call FSM all live inside the modem firmware (NON-HLOS.bin — qpSipUtils.cpp, sipDialog.cpp, qvp_rtp_evs_profile.c); the AP runs no SIP/RTP at all and just drives it over QMI. The IMS PDN comes up on rmnet_data1 with P-CSCF 2a01:2e0:1e:f050::4 / f040::4. And it's not carrier-gated in the modem — no Plus/Lyca carrier_policy.xml anywhere in the firmware; the only VoLTE gate was Android's carrier_config (carrier_volte_available_bool=false), overridden by persist.dbg.volte_avail_ovr=1. So an open stack only needs QMI for the IMS PDN + the voice session, not its own IMS core.

QMI surface (from the vendor IDL tables):

  • WDS 0x01START_NETWORK_INTERFACE 0x20, GET_RUNTIME_SETTINGS 0x2d (P-CSCF)
  • IMSS 0x12 — note: 0x12, not the usually-quoted 0x18
  • IMSA 0x21GET_REGISTRATION_STATUS 0x20, reg ind 0x2f
  • Voice 0x09 v02 — DIAL_CALL 0x20, ALL_CALL_STATUS_IND 0x2e

libqmi today has no IMSA and no IMSS service at all, and the Voice plugin is missing the IMS TLVs (call_type=VOICE_IP, manage_ip_calls, ALL_CALL_STATUS_IND) — a fairly contained set of codecs to add if MM/libqmi ever wants VoLTE here.

The audio half is the real gap, and I managed to fully pin the recipe from a live call (adb root + tinymix diff + the audio HAL logs). When the call goes active the HAL opens the VoiceMMode1 front-end PCM and starts an ADSP voice session keyed by a VSID:

FE PCM:   pcmC0D2 (VoiceMMode1), usecase "voicemmode1-call"
          RX pcmC0D2p PCM_OUT, TX pcmC0D2c PCM_IN, mono / 8000 Hz / 160 / 2
VSID:     0x11C05000   (set {0x10C01000,0x10DC1000,0x10C02000,0x10803000,0x11C05000,0x11DC5000})
ACDB:     send_voice_cal acdb_rx=10 acdb_tx=11   (headphones route)
Mixer:    RX_CDC_DMA_RX_0_Voice Mixer VoiceMMode1 = 1
          VoiceMMode1_Tx Mixer TX_CDC_DMA_TX_3_MMode1 = 1
          + RX_CDC_DMA_RX_0 / TX_CDC_DMA_TX_3 Power Mode + Island Config
HAL order: voice_extn_set_parameters -> update_calls(INACTIVE->ACTIVE)
           -> voice_start_usecase -> pcm_open RX+TX -> pcm_start
           -> audio_route_apply -> set sidetone/volume -> acdb_loader_send_voice_cal

One useful correction to my earlier post: the VSID is real and used — it just doesn't show up as the Voc VSID mixer control (that reads -1); it's passed through the voice-session API into the kernel voice driver. I've got the full per-device backend/ACDB table too (speaker WSA_CDC_DMA_RX_0 acdb 15, BT-SCO SLIMBUS_7, USB USB_AUDIO_RX), and confirmed BT-SCO call audio works on the stock image.

So on mainline the two missing pieces are concrete now:

  1. A SoC sound card — the Android DT is 100% downstream APR (qcom,msm-audio-apr + qcom,msm-pcm-voice + cdc-dma DAIs, sound card lahaina-yupikidp-snd-card), while mainline needs the gpr/q6apm/q6prm route + a machine node + the LPASS macro/codec clocks un-reserved (the qcm6490-idp / RB3 Gen2 work is the template).
  2. The voice routing itself — which on the GPR/AudioReach stack has no q6voice equivalent at all, so it's net-new: open the VoiceMMode-style FE, start the VSID session, push (or substitute) the ACDB voice cal, flip those mixer legs — all triggered off a ModemManager call-active signal.

I've written all of this up properly (the full recipe, the device/ACDB/backend table, the HAL disassembly order, the QMI message IDs + libqmi gaps, the DT diff) and have the decompiled Android IMS service + audio HAL + the live captures (mixer dumps, a 47 MB diag .qmdl, the base DTB). The board's on my bench with a working VoLTE call on Android, so I can grab any further captures (a full-mask QMI/SIP trace from a Linux host, the BT-SCO/handset ACDB ids, whatever) and beta-test DT/driver/UCM patches. If any of this is useful to the OpenIMSd folks or fits where you're taking the libqmi/MM VoLTE work, just point me at what'd help most and I'll dig.