<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>OpenIMSD</title><link href="https://www.openimsd.de/" rel="alternate"/><link href="https://www.openimsd.de/feeds/all.atom.xml" rel="self"/><id>https://www.openimsd.de/</id><updated>2026-05-14T12:42:00+02:00</updated><entry><title>Ringback tone</title><link href="https://www.openimsd.de/10-ringback-tone.html" rel="alternate"/><published>2026-05-14T12:42:00+02:00</published><updated>2026-05-14T12:42:00+02:00</updated><author><name>OpenIMSd team</name></author><id>tag:www.openimsd.de,2026-05-14:/10-ringback-tone.html</id><summary type="html">&lt;p&gt;How is the ringback tone generated?!&lt;/p&gt;</summary><content type="html">&lt;h1&gt;Ringback tone&lt;/h1&gt;
&lt;h2&gt;What is a ringback tone?&lt;/h2&gt;
&lt;p&gt;The ringback tone is a tone which originates from circuit switch old telephone
lines to tell the caller that the phone of the callee is ringing.&lt;/p&gt;
&lt;p&gt;It tells you when you call someone, that the phone at the other end is ringing.
For mobile networks, the behaviour is as much other things, well defined, but complex.&lt;/p&gt;
&lt;p&gt;While placing a VoLTE call, the ringback tone was missing on postmarketOS/Google Pixel 3a.&lt;/p&gt;
&lt;h3&gt;2G phone call / circuit-switched call&lt;/h3&gt;
&lt;p&gt;For 2G/GSM networks, it depends how the network is working and maybe even on the load of the network.
Eeg. a phone is calling a land line, the network can decide to save radio resources, it can assign the phone
the traffic channel for voice data (TCH) only &lt;em&gt;after&lt;/em&gt; the other side accepted the call.
However if it does this, there may some lost voice samples, because the network &amp;amp; phone needs some time to establish
the traffic channel.
In this case, the phone has to generate the ringback tone.&lt;/p&gt;
&lt;p&gt;But if the network has enough resources, it may assign the phone the traffic channel for voice data early
which improve the user experience, because there isn't the lost audio at the beginning.&lt;/p&gt;
&lt;h3&gt;4G phone call / VoLTE / packet-switched call&lt;/h3&gt;
&lt;p&gt;There are no traffic channel to save, but there is bandwidth, even a VoLTE calls uses only a little bit of bandwidth.
VoLTE is based on SIP (Session Initiation Protocol).
For SIP calls, the default case is the ringback tone generation is done on the phone.
There is a special case, called early media, where the network will generate the ringback tone,
but usually this is only used when giving you a custom ringback tone.&lt;/p&gt;
&lt;h2&gt;Qualcomm basebands&lt;/h2&gt;
&lt;p&gt;Back to OpenIMSd. When I started to do VoLTE calls with the Pixel 3a or Oneplus 6T, there were no
ringback tone.&lt;/p&gt;
&lt;p&gt;Qualcomm basebands has a special TLV for ringback tone generation in the voice call status messages,
I've not yet tested the exact behaviour with 2G networks yet,
but for 4G network, the baseband adds the TLV and tells the user space to generate the ringback tone.&lt;/p&gt;
&lt;h2&gt;Missing bits and peaces to get ringback tone generation for VoLTE calls&lt;/h2&gt;
&lt;p&gt;libqmi needs to decode the TLV, the QMI messages are already present, only this TLV wasn't known.
I've created a &lt;a href="https://gitlab.freedesktop.org/mobile-broadband/libqmi/-/merge_requests/462"&gt;merge request for libqmi&lt;/a&gt; to add it.&lt;/p&gt;
&lt;p&gt;ModemManager needs to announce the local ringback via d-bus. &lt;a href="https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/merge_requests/1450"&gt;Merge request for ModemManager&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I'm using phosh with postmarketOS on the device, so gnome-calls is connecting to ModemManager to handle the voice calls.
Thankfully there is already a ringback tone generation for SIP which I could use as well.
&lt;a href="https://gitlab.gnome.org/GNOME/calls/-/merge_requests/818"&gt;Merge Request for gnome-calls&lt;/a&gt;&lt;/p&gt;</content><category term="blog"/><category term="blog"/></entry><entry><title>QCOM baseband profile manager</title><link href="https://www.openimsd.de/09-qcom-baseband-profile-manager.html" rel="alternate"/><published>2026-01-26T23:42:00+01:00</published><updated>2026-01-26T23:42:00+01:00</updated><author><name>OpenIMSd team</name></author><id>tag:www.openimsd.de,2026-01-26:/09-qcom-baseband-profile-manager.html</id><summary type="html">&lt;p&gt;The QCOM baseband profile manager&lt;/p&gt;</summary><content type="html">&lt;h1&gt;QCOM baseband profile mangaer&lt;/h1&gt;
&lt;p&gt;I've written a profile manager for the QCOM baseband which talks to the PDC service.&lt;/p&gt;
&lt;p&gt;The PDC is the persistent device configuration.
It controls similar settings as the &lt;a href="https://source.android.com/docs/core/connect/carrier"&gt;carrier configuration on Android&lt;/a&gt;,
such as the APN, SMSC, the IMS functionality, the 5G functionality, ...
But the PDC profiles have more control over the baseband than the Android carrier configuration does.&lt;/p&gt;
&lt;p&gt;As far as I understand, those PDC profiles are loaded into the baseband also via the PDC, but are persistent and
usually updated via Android system updates. The PDC profiles are stored as foo.mbn files under various locations.&lt;/p&gt;
&lt;p&gt;I wonder if it is possible to update the PDC profiles without an Android update. I would have
expected an OTA mechanism which every other carrier/operator/provider can use. Maybe using OMA DM (Open Mobile Aliance Device Management).
But so far I've not found any evidence for this.&lt;/p&gt;
&lt;p&gt;The new qcom-baseband-profile-manager is a daemon which selects the "best" available profile for you without
any user interaction.
It is configured by a toml config file and selects the profile based on the simcard and the available
profiles. It also notices if the simcard changes and selects a different profile based on it.&lt;/p&gt;
&lt;p&gt;For further information see the &lt;a href="https://gitlab.postmarketos.org/modem/openimsd/qcom-baseband-profile-manager/-/blob/main/README.md"&gt;README.md&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There is also a &lt;a href="https://gitlab.postmarketos.org/modem/openimsd/apk-binary-repo"&gt;binary repo for postmarketos&lt;/a&gt; containing the package until the packages are available in the package repositories of alpine and postmarketos.&lt;/p&gt;
&lt;p&gt;The APKBUILD files are available at the &lt;a href="https://gitlab.alpinelinux.org/lynxis/aports/-/tree/openimsd"&gt;alpine gitlab as a fork of aports&lt;/a&gt; and &lt;a href="https://gitlab.postmarketos.org/modem/openimsd/pmaports"&gt;on the postmarketos gitlab as a fork of pmports&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;How to install qcom-baseband-profile-manager&lt;/h2&gt;
&lt;p&gt;You can either use python tooling (pip/poetry) to install it from the repo.
Or as alternative use alpine packages on a phone.&lt;/p&gt;
&lt;p&gt;To use alpine packages download the &lt;a href="https://gitlab.postmarketos.org/modem/openimsd/apk-binary-repo"&gt;binary repo&lt;/a&gt; onto the phone:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;
&lt;span class="nx"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//gitlab.postmarketos.org/modem/openimsd/apk-binary-repo&lt;/span&gt;
&lt;span class="nx"&gt;cp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;apk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;binary&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;pmos&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;692&lt;/span&gt;&lt;span class="nx"&gt;c9b1e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rsa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;apk&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="nx"&gt;apk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;apk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;binary&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;aarch64&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;py3&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;gsm0338&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;py3&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;osmocom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;py3&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;statemachine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;py3&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;qcom&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;baseband&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apk&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;copy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;
&lt;span class="nx"&gt;wget&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;https://gitlab.postmarketos.org/modem/openimsd/qcom-baseband-profile-manager/-/raw/main/pdc.toml?ref_type=heads&amp;amp;inline=false&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;pdc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toml&lt;/span&gt;
&lt;span class="nx"&gt;qcom&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;baseband&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;manager&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;qrtr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//0 -c /root/pdc.toml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="blog"/><category term="blog"/></entry><entry><title>How VoLTE is working</title><link href="https://www.openimsd.de/08-how-volte-is-working.html" rel="alternate"/><published>2025-10-20T23:42:00+02:00</published><updated>2025-10-20T23:42:00+02:00</updated><author><name>OpenIMSd team</name></author><id>tag:www.openimsd.de,2025-10-20:/08-how-volte-is-working.html</id><summary type="html">&lt;p&gt;How VoLTE is working on QMI&lt;/p&gt;</summary><content type="html">&lt;h1&gt;How does VoLTE works on Qualcomm based SoC?&lt;/h1&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Currently VoLTE isn't just working when your Qualcomm based smartphone is switch to LTE (at least for 95% of the phones).
The data connection works just fine, but voice calls either doesn't work at all or your
phone has to switch to 2G or 3G to do the voice calls.&lt;/p&gt;
&lt;p&gt;To reverse engineer VoLTE on Qualcomm SoCs, I've used an OnePlus 6T as a target which is based on an Qualcomm SDM845. The OnePlus 6T has been released in 2018. I'll use LineageOS instead of the vendor-provided Android OS,
because it makes it easier to get &lt;em&gt;root&lt;/em&gt; access. Further it allows to get a newer Android version onto the device.&lt;/p&gt;
&lt;p&gt;First of all, VoLTE works only when both Android and the baseband are working together.
I'll use the term &lt;strong&gt;Android&lt;/strong&gt; for software running on the APU (Application processor, the ARM processor) and
&lt;strong&gt;baseband&lt;/strong&gt; for the proprietary firmware running on the Hexagon DSP.&lt;/p&gt;
&lt;h2&gt;QMI&lt;/h2&gt;
&lt;p&gt;Most of the communication between the baseband and Android is done using QMI messages.&lt;/p&gt;
&lt;p&gt;A short recap on QMI:&lt;/p&gt;
&lt;p&gt;QMI has 3 types of messages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Request&lt;/li&gt;
&lt;li&gt;Response&lt;/li&gt;
&lt;li&gt;Indication&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="QMI Request/Response/Indication" src="https://www.openimsd.de/08_how_volte_is_working/08_qmi_messages.msc.png" title="message sequence chart of a QMI Request, Response and Indication"&gt;&lt;/p&gt;
&lt;p&gt;Android does a &lt;em&gt;request&lt;/em&gt; towards the baseband and it answers with a &lt;em&gt;response&lt;/em&gt;.
QMI has message ids to match a request to a response, allowing multiple requests and responses as the same time.&lt;/p&gt;
&lt;p&gt;An &lt;em&gt;indication&lt;/em&gt; is a message informing the Android side about a state change or an event.
An incoming call is an indication. Also if the network cell changes, an indication is sent to the Android. Some indication
are sent, without asking for, For others, a request must be used to enable them.&lt;/p&gt;
&lt;p&gt;You could describe QMI with the common server/client model:
The baseband is the &lt;strong&gt;server&lt;/strong&gt; and Android the &lt;strong&gt;client&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If there would be only a single service on the baseband, the amount of message types would be huge.
QMI offers multiple services instead. This also allows to have more contained
code. The service for the UIM (sim card) doesn't have to know anything about the radio frequencies.&lt;/p&gt;
&lt;p&gt;&lt;img alt="QMI Services" src="https://www.openimsd.de/08_how_volte_is_working/08_qmi_services.drawio.png" title="An overview of services"&gt;&lt;/p&gt;
&lt;p&gt;Android runs multiple daemons and each daemon can open a QMI connection to a service or to multiple
services at the same time.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.freedesktop.org/software/libqmi/libqmi-glib/latest/libqmi-glib-Common-enumerations-and-flags.html#QmiService"&gt;While libqmi supports many services,&lt;/a&gt; there are more. Also some OEMs may create additional vendor specific ones
Here are the most important services:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CTL (Control), controls the QMI connections, used to establish a connection towards a service&lt;/li&gt;
&lt;li&gt;DMS (Device Management Service), controls device power, e.g. airplane mode&lt;/li&gt;
&lt;li&gt;NAS (Network Access Service), access and selection of the mobile network&lt;/li&gt;
&lt;li&gt;UIM (User Identification Module), access to the sim card, enter the PIN&lt;/li&gt;
&lt;li&gt;WDS (Wireless Data Service), manage the data session, create, close&lt;/li&gt;
&lt;li&gt;WMS (Wireless Messaging Service), SMS, cell broadcast&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you know a public specification or documentation of QMI, please reach out to
me.&lt;/p&gt;
&lt;h2&gt;IP multimedia subsystem / VoLTE&lt;/h2&gt;
&lt;p&gt;The IMS functionality of the baseband consists of multiple QMI services, in difference to the other logical functions, which are usually concentrated into a single service.&lt;/p&gt;
&lt;p&gt;&lt;img alt="IMS QMI services" src="https://www.openimsd.de/08_how_volte_is_working/ims_structure.mermaid.png" title="Relation of IMS QMI Services. IMSA on top, all other service under."&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IMS Application (IMSA)&lt;/li&gt;
&lt;li&gt;IMS Settings (called IMS by libqmi)&lt;/li&gt;
&lt;li&gt;IMS Presence (IMSP)&lt;/li&gt;
&lt;li&gt;IMS RTP (IMSRTP)&lt;/li&gt;
&lt;li&gt;IMS DCM (IMSDCM)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;strong&gt;IMS application&lt;/strong&gt; is at the top level view of the current state of the IMS.
"Is the phone registered to VoLTE?"&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;IMS settings&lt;/strong&gt; service is the configuration endpoint, it allows settings many configurations options,
from basic enable VoLTE, over codec, over handover and many more.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;IMS presence&lt;/strong&gt; controls the SIP presence.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;IMS RTP&lt;/strong&gt; service, RTP settings, but no data frames (as long as I've seen). I've not looked into this service more deeply.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;IMS DCM&lt;/strong&gt; service, data control manager (not yet certain, what DCM stands for). But this is where many of the magic happens.&lt;/p&gt;
&lt;p&gt;At the moment, I expect I've to use only IMS application, settings and DCM services to have a fully functional IMS service.&lt;/p&gt;
&lt;h2&gt;IMS DCM&lt;/h2&gt;
&lt;p&gt;The DCM service doesn't fit into the common case of the baseband being the server and Android being a client, because it's &lt;strong&gt;vice versa&lt;/strong&gt;.
&lt;strong&gt;Android is the server and the baseband the client!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The baseband requests a data session for the VoLTE traffic over the DCM service.
Android will open a PDN session by talking again to the WDS service and responds back to the baseband.&lt;/p&gt;
&lt;h1&gt;Message sequence chart&lt;/h1&gt;
&lt;p&gt;&lt;img alt="VoLTE registration communication" src="https://www.openimsd.de/08_how_volte_is_working/08_how_volte_is_working.msc.png" title="message sequence chart of a VoLTE registration"&gt;&lt;/p&gt;
&lt;p&gt;There are a couple of unknown parts in there:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why is the modem opening the IMS PDN (on the LTE) before the &lt;em&gt;Start Network&lt;/em&gt; of the IMS daemon? Is there an unknown service? Or because of the PDC profile?&lt;/li&gt;
&lt;li&gt;Why are there calls towards WDS and QOS to bind the subscription and set the mux? Maybe legacy API and modern API?&lt;/li&gt;
&lt;li&gt;How does the IMSDCM daemon knows the IPv6 address? Does it sent the IPv6 RS and handles the RA (IP selection)?&lt;/li&gt;
&lt;li&gt;The IMSDCM sends back PDP Connection Id 0x14. Is this local generated by the IMSDCM or has it something to do with the WDS connection id? But if it would be the WDS connection id, how does it know it?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even with a couple unknowns left, I'm confident to have the required parts to implementing a daemon to talk to the IMS services.&lt;/p&gt;
&lt;h1&gt;What's next?&lt;/h1&gt;
&lt;p&gt;The next step is a PDC (Persistent Device Configuration) daemon, to select a baseband profile which supports
IMS.&lt;/p&gt;
&lt;p&gt;Afterwards I'll implement prototype openimsd prototype.&lt;/p&gt;</content><category term="blog"/><category term="blog"/></entry><entry><title>QMI trace of VoLTE registration</title><link href="https://www.openimsd.de/07-qmi-trace-volte-registration.html" rel="alternate"/><published>2025-10-18T16:00:00+02:00</published><updated>2025-10-18T16:00:00+02:00</updated><author><name>Alexander 'lynxis' Couzens &lt;lynxis@fe80.eu&gt;</name></author><id>tag:www.openimsd.de,2025-10-18:/07-qmi-trace-volte-registration.html</id><summary type="html">&lt;p&gt;A pcap trace of the VoLTE registration under different circumstances&lt;/p&gt;</summary><content type="html">&lt;p&gt;TLDR; &lt;a href="https://www.openimsd.de/07_qmi_trace_of_volte_registration.tar.gz"&gt;the trace&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To document and improve the understanding of VoLTE, I've created pcap traces of the QMI message
of the VoLTE registration.&lt;/p&gt;
&lt;p&gt;The original pcap file has been created using a Oneplus 6T running lineageos/Android with
a german 1&amp;amp;1 simcard of the brand maxxim, which uses the Vodafone network in Germany.&lt;/p&gt;
&lt;p&gt;To capture the traffic I've used &lt;a href="https://github.com/fgsect/scat"&gt;SCAT&lt;/a&gt; and &lt;a href="https://www.openimsd.de/qmi-sniffing-with-frida.html"&gt;qmi-frida-tracer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;SCAT is used to capture radio packets within the same trace, it both exports NAS-EPS (radio management),
but also user traffic. It makes it easier to put QMI messages into relation with messages
on the radio side and improve the understanding.
E.g. a NAS-EPS or SIP message trigger an event on the QMI side.
Also if a QMI TLV is unknown, we might use the radio message to decipher it.&lt;/p&gt;
&lt;p&gt;qmi-frida-tracer was ran against the following daemons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;qcrild (first pid)&lt;/li&gt;
&lt;li&gt;qcrild (second pid)&lt;/li&gt;
&lt;li&gt;imsdatadaemon&lt;/li&gt;
&lt;li&gt;imsqmidaemon&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I've put the Oneplus 6T into an usb mode supporting all 3: diag,adb and rmnet.&lt;/p&gt;
&lt;p&gt;rmnet is required to use qmicli later to set the correct PDC profile to allow the
Android to register to VoLTE.&lt;/p&gt;
&lt;h2&gt;Test setup&lt;/h2&gt;
&lt;p&gt;The setup consist of 2x VMs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1x VM where all the tools ran and the Oneplus 6T is connected (192.168.56.108)&lt;/li&gt;
&lt;li&gt;1x VM where the traffic is captured and wireshark runs. (192.168.56.1)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Put the Oneplus 6T into the correct usb mode:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;adb root; sleep 1; adb shell setprop sys.usb.config diag,serial_cdev,rmnet,adb&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Running &lt;a href="https://github.com/fgsect/scat"&gt;SCAT&lt;/a&gt; (git version 7eac9c5bea) with:
&lt;code&gt;scat -u -t qc -v 0x05c6 -p 0x9091 -i 0 -H 192.168.56.1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;qmi-frida-trace to pass traffic to 192.168.56.1 (modify line where 127.0.0.1 is set as target):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;./qmi-frida-tracer.py -n imsdatadaemon
./qmi-frida-tracer.py -n imsqmidaemon
./qmi-frida-tracer.py -p 1584 # first qcrild pid
./qmi-frida-tracer.py -p 1598 # second qcrild pid
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Additional I'll set later a VoLTE capable PDC profile using
&lt;code&gt;qmicli -d /dev/cdc-wdm0 --pdc-activate-config=software,2F:1D:0C:4C:28:2D:50:FC:03:84:D8:9E:F3:92:81:C7:DB:A5:1A:7E&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Modification of the trace&lt;/h2&gt;
&lt;p&gt;For privacy reasons the pcap file which has been published here, has been filtered, but the essential parts are present.
All packets which contain either the IMSI, IMEI, phone number or location have been removed.
Further:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Service 0x44 was removed. The service is still unknown, but not suspected to interact with IMS (yet).&lt;/li&gt;
&lt;li&gt;All radio packets (from SCAT) except a couple ones, have been removed, because most of them contains privacy related informations.&lt;/li&gt;
&lt;li&gt;All data packets have been removed including SIP, RTP traffic, because those contains both the IMEI, phone number and called phone number.&lt;/li&gt;
&lt;li&gt;The responses of the simcard (service: uim) to &lt;em&gt;Read Transparent File&lt;/em&gt; or &lt;em&gt;Read Record&lt;/em&gt; and request/responses of &lt;em&gt;Send APDU&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;NAS: Get Signal Info has been removed, because it clutters the pcap.&lt;/li&gt;
&lt;li&gt;Voice service: &lt;em&gt;Dial Call&lt;/em&gt; and &lt;em&gt;All Call Status&lt;/em&gt; were removed, but no IMS specific parts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To get the most use out of the pcap, please use the wireshark dissector embedded into the .tar.gz of the pcap.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wireshark -X lua_script:qmi_dissector_gen.lua -X lua_script:scat.lua ./trace_volte.pcapng&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I've commented on a couple packets, there are some QMI messages which are unknown to libqmi,
I've looked them up, but all aren't related. To most I've added comments to the packet in the pcap.
I recommend adding the &lt;strong&gt;comment&lt;/strong&gt; coloumn to the packet overview.
It is possible to use the source port of the UDP/GSMTAP message to determine which process received or sent
the QMI message.&lt;/p&gt;
&lt;p&gt;From the traffic I deduct:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;44303: imsdatadaemon&lt;/li&gt;
&lt;li&gt;39675: qcrild (it could be also imsqmidaemon, but the traffic contains to much additional traffic)&lt;/li&gt;
&lt;li&gt;58075: qcrild&lt;/li&gt;
&lt;li&gt;49595: scat (radio packets)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The most interesting parts of the pcap are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;filter &lt;code&gt;qmi.service_id == 0x0012 || qmi.service_id == 0x302&lt;/code&gt; showing IMS subsystem traffic&lt;/li&gt;
&lt;li&gt;filter &lt;code&gt;udp.srcport == 44303&lt;/code&gt; showing traffic of the imsdatadaemon&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you would like to know more about the pcap or have questions about the removed parts, you can reach us by
the matrix channel &lt;em&gt;#openimsd:postmarketos.org&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Test report&lt;/h2&gt;
&lt;p&gt;All times in UTC:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;10:48: Reboot phone&lt;/li&gt;
&lt;li&gt;10:49: Put phone into airplane without simcard, enable adb root, switch mode to rmnet/qmi + diag + adb&lt;/li&gt;
&lt;li&gt;10:49: start scat&lt;/li&gt;
&lt;li&gt;10:50: attach frida to current rild processes&lt;/li&gt;
&lt;li&gt;10:54: put simcard into the phone&lt;/li&gt;
&lt;li&gt;11:01: enter required pin 4556&lt;/li&gt;
&lt;li&gt;11:02: check if qcrild still have correct pids (sometime they change)&lt;/li&gt;
&lt;li&gt;11:15: enable frida also for imsqmidaemon &amp;amp; imsdatadaemon&lt;/li&gt;
&lt;li&gt;11:18: disable airplane&lt;/li&gt;
&lt;li&gt;11:20: enable airplane&lt;/li&gt;
&lt;li&gt;11:23: check if qcrild still have correct pids (sometime they change)&lt;/li&gt;
&lt;li&gt;11:24: libqmi: set pdc profile, failed CID allocation failed. timed out&lt;/li&gt;
&lt;li&gt;11:25: qmicli: pdc: exceute monitor-refresh, get software list, re-do libqmi set pdc profile again, works now&lt;/li&gt;
&lt;li&gt;11:26: disable airplane&lt;/li&gt;
&lt;li&gt;11:30: check VoLTE registration via phone info debug ui (&lt;code&gt;*#*#4636#*#*&lt;/code&gt;), VoLTE registered!&lt;/li&gt;
&lt;li&gt;11:39: calling another phone&lt;/li&gt;
&lt;li&gt;11:42: get called by another phone&lt;/li&gt;
&lt;li&gt;11:43: terminate call&lt;/li&gt;
&lt;li&gt;11:44: enable airplane&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;wireshark filter&lt;/h2&gt;
&lt;p&gt;To filter for privacy I used the following chaos filter:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;((frame.marked == true) || ((((!(qmi.service_id == 0x0047) ) &amp;amp;&amp;amp; !( (qmi.message_id == 0x0039) &amp;amp;&amp;amp; qmi.service_id == 0x0b)) &amp;amp;&amp;amp; !((qmi.message_id == 0x0020) &amp;amp;&amp;amp; (qmi.service_id == 0xb) &amp;amp;&amp;amp; (qmi.trans_response == 1)) &amp;amp;&amp;amp; !((qmi.message_id == 0x0021) &amp;amp;&amp;amp; (qmi.service_id == 0x0b)  &amp;amp;&amp;amp; (qmi.trans_response == 1)) &amp;amp;&amp;amp; !((qmi.message_id == 0x0024) &amp;amp;&amp;amp; (qmi.service_id == 0x02)  &amp;amp;&amp;amp; (qmi.trans_response == 1))) &amp;amp;&amp;amp; !lte_rrc &amp;amp;&amp;amp;  !((qmi.message_id == 0x0043) &amp;amp;&amp;amp; (qmi.service_id == 0x03)  &amp;amp;&amp;amp; (qmi.trans_response == 1)) &amp;amp;&amp;amp;  !((qmi.message_id == 0x004d || qmi.message_id == 0x00ac) &amp;amp;&amp;amp; (qmi.service_id == 0x03)  &amp;amp;&amp;amp; (qmi.trans_response == 1)) &amp;amp;&amp;amp;  !(((qmi.message_id == 0x0082) || (qmi.message_id == 0x00c3 || qmi.message_id == 0x004e || qmi.message_id == 0x0051) ) &amp;amp;&amp;amp; (qmi.service_id == 0x03)  &amp;amp;&amp;amp; (qmi.trans_indication == 1)) &amp;amp;&amp;amp; ((qmi.message_id == 0x003b) &amp;amp;&amp;amp; (qmi.service_id == 0x0b))  &amp;amp;&amp;amp; !gsm_a.ccch  &amp;amp;&amp;amp; !lte_rrc  &amp;amp;&amp;amp; (!qmi.service_id ==0x44) &amp;amp;&amp;amp; !evs &amp;amp;&amp;amp; !amr &amp;amp;&amp;amp; !udp.port == 47290 &amp;amp;&amp;amp; !(ip.flags.mf == True) &amp;amp;&amp;amp; !lte_rrc &amp;amp;&amp;amp; !nas-eps))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://www.openimsd.de/07_qmi_trace_of_volte_registration.tar.gz"&gt;The trace&lt;/a&gt;&lt;/p&gt;</content><category term="blog"/><category term="blog"/></entry><entry><title>VoLTE test setup</title><link href="https://www.openimsd.de/06-VoLTE-test-setup.html" rel="alternate"/><published>2025-09-09T16:00:00+02:00</published><updated>2025-09-09T16:00:00+02:00</updated><author><name>Alexander 'lynxis' Couzens &lt;lynxis@fe80.eu&gt;</name></author><id>tag:www.openimsd.de,2025-09-09:/06-VoLTE-test-setup.html</id><summary type="html">&lt;p&gt;The VoLTE test setup&lt;/p&gt;</summary><content type="html">&lt;p&gt;To improve testing and developing with VoLTE and LTE,
I've created a full VoLTE setup in a single VM including
the EPC.
The scripts to create the VM can be found on the &lt;a href="https://gitlab.postmarketos.org/modem/openimsd/create-volte-setup"&gt;gitlab of postmarketOS&lt;/a&gt;.
A full debian image containing the network with PLMN 901/70 can be downloaded &lt;a href="https://www.openimsd.de/release/volte.v0.1.0.qcow2.xz"&gt;from the blog&lt;/a&gt;. Additional documentation is also available in the project repository.&lt;/p&gt;
&lt;p&gt;Everything on the graph except the eNodeB is in the VM:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A graph of a classic 4G EPC including a IMS for VoLTE" src="https://www.openimsd.de/06_volte_vm/06_volte_vm-graph.jpg" title="VoLTE setup"&gt;&lt;/p&gt;
&lt;p&gt;The setup is build with the following software components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MME: open5gs-mmed&lt;/li&gt;
&lt;li&gt;SGW: open5gs-sgwcd/sgwud&lt;/li&gt;
&lt;li&gt;PGW: open5gs-amfd/upfd&lt;/li&gt;
&lt;li&gt;HSS: pyhss&lt;/li&gt;
&lt;li&gt;IMS: kamailio as p-cscf, i-cscf, s-cscf&lt;/li&gt;
&lt;li&gt;PBX: asterisk&lt;/li&gt;
&lt;li&gt;dns: dnsmasq to offer dns overrides&lt;/li&gt;
&lt;li&gt;redis: pyhss internal communication&lt;/li&gt;
&lt;li&gt;mariadb: pyhss &amp;amp; kamailio persistent db&lt;/li&gt;
&lt;li&gt;pyhss-cli: a simple cli to manage pyhss subscribers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The VM network setup is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;first interface: dhcp client. This interface will be used to nat UE traffic and for the VM iteself.&lt;/li&gt;
&lt;li&gt;second interface: RAN interface, to connect a eNodeB (static addresses, no dhcp)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you've a good SDR and GPSDO available, you can also use &lt;a href="https://docs.srsran.com/projects/project/en/latest/"&gt;srsRAN&lt;/a&gt; to create a eNodeB.&lt;/p&gt;</content><category term="blog"/><category term="blog"/></entry><entry><title>PyHSS and PyHSS cli</title><link href="https://www.openimsd.de/05-PyHSS.html" rel="alternate"/><published>2025-09-09T14:59:00+02:00</published><updated>2025-09-09T14:59:00+02:00</updated><author><name>Alexander 'lynxis' Couzens &lt;lynxis@fe80.eu&gt;</name></author><id>tag:www.openimsd.de,2025-09-09:/05-PyHSS.html</id><summary type="html">&lt;p&gt;Introduction of PyHSS and PyHSS-cli&lt;/p&gt;</summary><content type="html">&lt;p&gt;One of the milestone of the journey to a good VoLTE support is
a test setup containing a fully working LTE and VoLTE network.&lt;/p&gt;
&lt;p&gt;While doing so, I've came across PyHSS, a 4G HSS (central database), which will be used
in the test setup.
However the problem of PyHSS is, there wasn't any good tooling to interact with.
PyHSS itself offers a REST interface, but not cli.
There is a webinterface written by &lt;a href="https://github.com/zarya/PyHSS-gui"&gt;zarya - pyhss-gui&lt;/a&gt;
but it requires nodejs and couple of npm dependencies.&lt;/p&gt;
&lt;p&gt;So I've started writing a minimal cli in python using click and httpx.&lt;/p&gt;
&lt;p&gt;Even though PyHSS offers a swagger.json I went manual implemented those calls,
because parsing the swagger.json also requires a lot of dependencies.
Further only a couple API calls are needed to get basic subscriber management.
The project can be found on &lt;a href="https://codeberg.org/lynxis/PyHSS-cli"&gt;https://codeberg.org/lynxis/PyHSS-cli&lt;/a&gt;&lt;/p&gt;</content><category term="blog"/><category term="blog"/></entry><entry><title>Activating Voice over LTE on Android</title><link href="https://www.openimsd.de/android-activate-volte-qmi-pdc-service.html" rel="alternate"/><published>2025-06-22T01:23:00+02:00</published><updated>2025-06-22T01:23:00+02:00</updated><author><name>Alexander 'lynxis' Couzens &lt;lynxis@fe80.eu&gt;</name></author><id>tag:www.openimsd.de,2025-06-22:/android-activate-volte-qmi-pdc-service.html</id><summary type="html">&lt;p&gt;How to activate VoLTE on Android using PDC&lt;/p&gt;</summary><content type="html">&lt;p&gt;While looking into VoLTE for Android, you find tons of manuals, how to enable it on a phone
where it is disabled.&lt;/p&gt;
&lt;p&gt;The main parts are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;enable root (depends, sometime optional)&lt;/li&gt;
&lt;li&gt;enable QC diag on USB&lt;/li&gt;
&lt;li&gt;use the PDC tool on a lapop to switch the profile (windows, QXDM tool)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This usually works until the next sim switch or sometimes reboot of the phone.&lt;/p&gt;
&lt;h1&gt;What is the PDC?&lt;/h1&gt;
&lt;p&gt;PDC stands for Persistent Device Configuration.
libqmi already supports talking to the PDC.&lt;/p&gt;
&lt;h2&gt;But how does it work?&lt;/h2&gt;
&lt;p&gt;Since every HowTo tells activate QC Diag, it must depend on QC Diag.
QC Diag seems a door to a world with thousands doors and services
(like in the matrix).&lt;/p&gt;
&lt;p&gt;I'm using a Oneplus 6t with LineageOS and root enabled for this.
Enable diag via: &lt;code&gt;adb root; sleep 1; adb shell setprop sys.usb.config diag,adb&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Somehow it didn't worked directly. The QXDM tools connected to my device,
but the PDC tool didn't.&lt;/p&gt;
&lt;p&gt;Later I tried a different command:
&lt;code&gt;adb root; sleep 1; adb shell setprop sys.usb.config diag,serial_cdev,rmnet,adb&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now the PDC tools shows it, but it doesn't connect, but with this error:&lt;/p&gt;
&lt;p&gt;&lt;img alt="The PDC tool running and showing a warning: &amp;quot;QueryListConfigs failed with Gobi error code: 16(Malformed QMI response received)&amp;quot;" src="https://www.openimsd.de/04_activating_android_volte/pdc_qmi_error.png" title="qmierrormessage"&gt;&lt;/p&gt;
&lt;p&gt;So it is using QMI, but over Diag?&lt;/p&gt;
&lt;p&gt;That's very interesting. It would be very helpful while developing to access the QMI
port from the dev machine instead of going over the phone OS.
Also it would allow accessing it while running Android to peek and poke around.&lt;/p&gt;
&lt;p&gt;After a reboot of the phone, it started to work with this adb line
&lt;code&gt;adb shell setprop sys.usb.config diag,serial_cdev,rmnet,adb&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And shows all profiles.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The PDC tool running and working. The tool is connected to the Device: &amp;quot;Qualcomm Wireless HS-USB Ethernet Adapter 9091&amp;quot;.
In the middle is a table with the following columns: &amp;quot;Description, Type, Size, Sub0, Sub1, Sub2, Version, ID&amp;quot; and showing 
the profiles" src="https://www.openimsd.de/04_activating_android_volte/pdc_working.png"&gt;&lt;/p&gt;
&lt;p&gt;So how does it work?&lt;/p&gt;
&lt;p&gt;I have a USB sniffer around, a &lt;a href="https://greatscottgadgets.com/cynthion/"&gt;Cynthion&lt;/a&gt; (you could also use a Windows VM but since I've this tool around, I want to use it).&lt;/p&gt;
&lt;p&gt;&lt;img alt="Showing a test setup of 2 laptops, 1 smartphone and 1 cynthion. The laptops and smartphone are connected to the cynthion" src="https://www.openimsd.de/04_activating_android_volte/pdc_hw_setup.jpg" title="TestSetup"&gt;&lt;/p&gt;
&lt;p&gt;My Cynthion was already flashed with recent firmware. I use &lt;a href="https://packetry.readthedocs.io/en/stable/what_is_packetry.html"&gt;packetry&lt;/a&gt;
to record the trace.&lt;/p&gt;
&lt;p&gt;Paketry saves the trace as pcap and can be also used with wireshark.&lt;/p&gt;
&lt;p&gt;While looking into it, I saw those frames in the usb output, with readable strings of the
PDC profiles. My wireshark filter is just "usb".&lt;/p&gt;
&lt;p&gt;&lt;img alt="wireshark-with-usb-volte-profile" src="https://www.openimsd.de/04_activating_android_volte/wireshark_volte_packet.png"&gt;&lt;/p&gt;
&lt;p&gt;In the lower corner, in the hexview you can find a name of a carrier profile: &lt;code&gt;TGL_Comb_Attach-Lab-CMCC&lt;/code&gt;
Now I'm looking into the whole USB packet.&lt;/p&gt;
&lt;p&gt;Exporting the control USB packet from wireshark to look more into it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;01500080240104280028004400010200000010040026000000110400&lt;/span&gt;&lt;span class="n"&gt;b86500001219001854474c5f436f6d625f4174746163682d4c61622d434d4343130400462001081404000000000016040046200108&lt;/span&gt;

&lt;span class="mf"&gt;014&lt;/span&gt;&lt;span class="n"&gt;c008024010429002800400001020000001004002700000011040004b90000121500144e53494f545f566f4c54452d4c61622d434d4343130400552001081404000000000016040055200108&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now I'm trying to figure out, where the QMI header is in this packet start.
Starting with the QMI header from &lt;code&gt;libqmi/src/libqmi-glib/qmi-message.c&lt;/code&gt; (git rev: 22405e4c9e1d8f10748f085fc2ea4aa411cdd3aa).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;struct full_message {
      guint8 marker;
      union {
          struct qmux_header qmux;
          struct qrtr_header qrtr;
      } header;
      union {
          struct control_message control;
          struct service_message service;
      } qmi;
} PACKED;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Replacing the unions by using QMUX and a service,
because it must be a service. It could be also a QRTR message instead of QMUX, but
I'll start trying to use QRTR.
The struct will look:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;struct full_message {
    guint8 marker;
    struct qmux_header {
        guint16 length;
        guint8 flags;
        guint8 service;
        guint8 client;
    }
    struct service_message {
        struct service_header {
            guint8 flags;
            guint16 transaction;
            guint16 message;
            guint16 tlv_length;
        }
        struct tlv tlv[];
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Taking the hex reprensentation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;01500080240104280028004400010200000010040026000000110400&lt;/span&gt;&lt;span class="n"&gt;b86500001219001854474c5f436f6d625f4174746163682d4c61622d434d4343130400462001081404000000000016040046200108&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It contains 81 byte or 0x51 bytes.
Other infos we might know is the PDC service number (from libqmi).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;libqmi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;glib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;qmi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;enums&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;
&lt;span class="n"&gt;QMI_SERVICE_PDC&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x24&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Looking for a 0x24 in the data:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;50&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;24&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;04280028004400010200000010040026000000110400&lt;/span&gt;&lt;span class="n"&gt;b86500001219001854474c5f436f6d625f4174746163682d4c61622d434d4343130400462001081404000000000016040046200108&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Merging&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;guint8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;marker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;qmux_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;guint16&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="n"&gt;gth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x0050&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;guint8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;guint8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x24&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;guint8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This looks already very good.
The length 0x0050 is only 1 byte of our full length. Also the marker is always set to 0x01.
But there isn't any data in front it. I would have expected some Diag encapsulation or prefix. Typical 0x7e in front of it or otherwise
escaped.&lt;/p&gt;
&lt;p&gt;Trying to validate the QMI data further.
QMI is using TLV (Type-Length-Data) to split the values.
With 1 byte type, 2 byte length, 'length' bytes value and QMI is usually encoding those sorted
by the type.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tlv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;guint8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;guint16&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nx"&gt;guint8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;[01] [5000 80 24 01] [04 2800 2800 4400] [010200000010040026000000110400b86500001219001854474c5f436f6d625f4174746163682d4c61622d434d4343130400462001081404000000000016040046200108]&lt;/span&gt;

&lt;span class="na"&gt;// Merging it into the struct&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;guint8 marker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;0x01&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;struct qmux_header {&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;guint16 length;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;0x0050&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;guint8 flags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;0x80&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;guint8 service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;0x24&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;guint8 client&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;0x01&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;struct service_message {&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;struct service_header {&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;guint8 flags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;0x04&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;guint16 transaction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;0x0028&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;guint16 message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;0x0028&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="na"&gt;guint16 tlv_length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;0x0044&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="na"&gt;struct tlv tlv[]; // &amp;lt;- this should be 0x0044 bytes long, which also matches our remaining data.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="na"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Decoding further by hand:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;(Only TLV part: T Length Value)
[01 0200 0000   10 0400 26000000 11 0400 b8650000 12 1900 1854474c5f436f6d625f4174746163682d4c61622d434d4343 13 0400 46200108 14 0400 00000000 16 0400 46200108]

Type   Len  Value
    01 0200 0000
    10 0400 27000000
    11 0400 04b90000
    12 1500 144e53494f545f566f4c54452d4c61622d434d4343
    13 0400 55200108
    14 0400 00000000
    16 0400 55200108
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So is correct and matches \o/.
But why isn't there a QC Diag header in front?&lt;/p&gt;
&lt;p&gt;The pcap shows the packets are exchanged with endpoint: (wIndex 0x2 which is part of the 2nd Interface).
To know more about this endpoint, we must look at the USB descriptor and the choosen configuration.
Look for the &lt;code&gt;GET DESCRIPTOR Response CONFIGURATION&lt;/code&gt; packet, here is a wireshark snippet from the trace:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;USB&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Link&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Layer&lt;/span&gt;
&lt;span class="n"&gt;USB&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;URB&lt;/span&gt;
&lt;span class="n"&gt;CONFIGURATION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DESCRIPTOR&lt;/span&gt;
&lt;span class="n"&gt;INTERFACE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;DESCRIPTOR &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Vendor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Specific&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bLength&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bDescriptorType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x04&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INTERFACE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bInterfaceNumber&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bAlternateSetting&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bNumEndpoints&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bInterfaceClass&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Vendor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Specific &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bInterfaceSubClass&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0xff&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bInterfaceProtocol&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x30&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;iInterface&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;ENDPOINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DESCRIPTOR&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bLength&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bDescriptorType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x05&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bEndpointAddress&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x81&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;IN&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Endpoint&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bmAttributes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x02&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;wMaxPacketSize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;512&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bInterval&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;ENDPOINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DESCRIPTOR&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bLength&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bDescriptorType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x05&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bEndpointAddress&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x01&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;OUT&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Endpoint&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bmAttributes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x02&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;wMaxPacketSize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;512&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bInterval&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;INTERFACE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;DESCRIPTOR &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Vendor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Specific&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bLength&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bDescriptorType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x04&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;INTERFACE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bInterfaceNumber&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bAlternateSetting&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bNumEndpoints&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bInterfaceClass&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Vendor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Specific &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bInterfaceSubClass&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x00&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bInterfaceProtocol&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x00&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;iInterface&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;UNKNOWN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DESCRIPTOR&lt;/span&gt;
&lt;span class="n"&gt;UNKNOWN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DESCRIPTOR&lt;/span&gt;
&lt;span class="n"&gt;UNKNOWN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DESCRIPTOR&lt;/span&gt;
&lt;span class="n"&gt;UNKNOWN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DESCRIPTOR&lt;/span&gt;
&lt;span class="n"&gt;ENDPOINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DESCRIPTOR&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bLength&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bDescriptorType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x05&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bEndpointAddress&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x83&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;IN&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Endpoint&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bmAttributes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x03&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;wMaxPacketSize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bInterval&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;
&lt;span class="n"&gt;ENDPOINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DESCRIPTOR&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bLength&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bDescriptorType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x05&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bEndpointAddress&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x82&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;IN&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Endpoint&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bmAttributes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x02&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;wMaxPacketSize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;512&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bInterval&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;ENDPOINT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DESCRIPTOR&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bLength&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bDescriptorType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x05&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bEndpointAddress&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x02&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;OUT&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Endpoint&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;our&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;target.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Scroll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;up&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;interface&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;belongs.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="kc"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;....&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Direction&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;OUT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Endpoint&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;....&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0010&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Endpoint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x2&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bmAttributes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x02&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;wMaxPacketSize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;512&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;bInterval&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So our endpoint 0x02 is part of the bInterface 1 with the interface class "Vendor Specific (0xff)", bInterfaceSubClass: 0x00, bInterfaceProtocol: 0x00.&lt;/p&gt;
&lt;p&gt;After looking into it further. This interface is the &lt;code&gt;rmnet&lt;/code&gt; part the &lt;code&gt;adb shell setprop sys.usb.config diag,serial_cdev,rmnet,adb&lt;/code&gt;.
rmnet is the same mode which most of the Qualcomm based mini-pcie and m.2 devices uses over USB.&lt;/p&gt;
&lt;p&gt;libqmi can just interact with it if the linux kernel is exposing it as /dev/cdc-wdm.
The linux kernel depends on the usb id and the amount of interface.&lt;/p&gt;
&lt;p&gt;Sadly I hoped to find a path to QMI over QC Diag.&lt;/p&gt;
&lt;h2&gt;Unlock VoLTE by using libqmi on Linux&lt;/h2&gt;
&lt;p&gt;Now putting everything together:
We will use a Linux on a Laptop to change the profile via QMI on a OnePlus6T with LineageOS on it (and root).&lt;/p&gt;
&lt;p&gt;I've inserted a different Simcard into the Oneplus 6T and VoLTE doesn't work.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;qmicli -d /dev/cdc-wdm0 --pdc-list-configs=software

And looking for the active profile:

Configuration 2:
    Description: Oversea-Commercial_DS
    Type:        software
    Size:        78772
    Status:      Active
    Version:     0x801F13A
    ID:          87:CA:4A:4D:52:48:CF:BF:13:2A:DB:C4:08:B2:2A:29:E0:CA:A5:67
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Ok, the &lt;code&gt;Oversae-Commercial_DS&lt;/code&gt; doesn't work.
Here is a complete list of my device:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;qmicli -d /dev/cdc-wdm0 --pdc-list-configs=software
Total configurations: 25
Configuration 1:
    Description: Telefonica_UK_Commercial
    Type:        software
    Size:        107336
    Status:      Inactive
    Version:     0x8010C9D
    ID:          E6:84:2A:02:AE:DA:D1:1A:08:14:E1:F4:CF:93:ED:6D:E9:7E:68:DC

Configuration 2:
    Description: Norway_Telia_Commercial
    Type:        software
    Size:        109448
    Status:      Active
    Version:     0x8012432
    ID:          BA:0F:0B:0E:9E:BC:0C:38:29:7F:C0:D9:AB:B9:9C:80:9E:7D:B2:AB

Configuration 3:
    Description: Oversea-Commercial_DS
    Type:        software
    Size:        78772
    Status:      Inactive
    Version:     0x801F13A
    ID:          87:CA:4A:4D:52:48:CF:BF:13:2A:DB:C4:08:B2:2A:29:E0:CA:A5:67

Configuration 4:
    Description: Telenor_Denmark_Commercial
    Type:        software
    Size:        105796
    Status:      Inactive
    Version:     0x8014326
    ID:          75:69:C0:4B:1C:63:AF:5C:3E:5A:9C:AA:0E:6D:BF:9A:D1:BB:64:D7

Configuration 5:
    Description: ROW_Commercial
    Type:        software
    Size:        45556
    Status:      Inactive
    Version:     0x8010809
    ID:          81:8A:AB:E9:B5:CD:F9:5C:89:FB:04:06:ED:E4:6C:6B:85:D7:20:3A

Configuration 6:
    Description: YTL_Commercial
    Type:        software
    Size:        49412
    Status:      Inactive
    Version:     0x8012D0C
    ID:          30:7F:09:F0:1B:0A:CD:A1:63:5C:BC:F8:AA:BB:5E:AC:18:48:D1:17

Configuration 7:
    Description: TaiwanMobile_Commercial
    Type:        software
    Size:        99208
    Status:      Inactive
    Version:     0x8014108
    ID:          3C:C1:1C:E0:A5:9B:73:C2:1E:8E:D6:0D:87:20:2E:DB:78:D8:91:0A

Configuration 8:
    Description: FarEastOne_Taiwan_Commercial
    Type:        software
    Size:        105312
    Status:      Inactive
    Version:     0x8014009
    ID:          BA:13:DC:4D:F5:3D:47:73:17:62:21:B8:2F:09:F5:2B:22:37:74:25

Configuration 9:
    Description: ChunghwaTel_Taiwan_Commercial
    Type:        software
    Size:        104792
    Status:      Inactive
    Version:     0x8014F09
    ID:          56:6E:CB:75:81:FA:C3:3F:65:46:8F:8F:16:2D:8F:D3:4D:5A:06:9C

Configuration 10:
    Description: MTNL-BSNL
    Type:        software
    Size:        78088
    Status:      Inactive
    Version:     0x801ED31
    ID:          3A:67:96:BB:B4:E1:07:40:FB:75:11:A2:E0:1A:5E:EC:27:C8:77:64

Configuration 11:
    Description: Volte_OEM_Lab
    Type:        software
    Size:        71016
    Status:      Inactive
    Version:     0x801F221
    ID:          D8:EF:7F:E2:16:24:38:15:AB:FD:BA:CE:2F:B6:A8:A3:49:30:C4:62

Configuration 12:
    Description: Volte_OEM_PTCRB
    Type:        software
    Size:        58864
    Status:      Inactive
    Version:     0x801EE2D
    ID:          0D:14:B5:E9:D2:52:AA:21:6C:FD:7D:C0:63:91:B8:6F:38:6F:CF:D3

Configuration 13:
    Description: CDMAless-Verizon
    Type:        software
    Size:        109344
    Status:      Inactive
    Version:     0x8010108
    ID:          FE:D6:7A:D3:0C:2F:8A:49:B0:A3:00:9C:80:09:A5:84:47:83:FA:7B

Configuration 14:
    Description: Commercial-TMO
    Type:        software
    Size:        70656
    Status:      Inactive
    Version:     0x8010536
    ID:          2F:1D:0C:4C:28:2D:50:FC:03:84:D8:9E:F3:92:81:C7:DB:A5:1A:7E

Configuration 15:
    Description: VoLTE-ATT
    Type:        software
    Size:        69028
    Status:      Inactive
    Version:     0x8010348
    ID:          12:A1:7C:8A:11:6E:A9:2F:D1:A7:98:57:AB:7A:E7:A3:E0:71:D5:99

Configuration 16:
    Description: FirstNet
    Type:        software
    Size:        69084
    Status:      Inactive
    Version:     0x8010337
    ID:          E6:E0:E0:E4:52:27:E0:C8:FC:6D:41:6D:FD:6D:31:E1:29:AC:C5:9E

Configuration 17:
    Description: Netherlands-VoLTE-Vodafone
    Type:        software
    Size:        105220
    Status:      Inactive
    Version:     0x80104F6
    ID:          EC:5E:DD:F2:73:13:73:23:1A:04:00:D6:F6:17:EB:FA:D3:56:A7:B5

Configuration 18:
    Description: Telia_Sweden
    Type:        software
    Size:        108948
    Status:      Inactive
    Version:     0x8012411
    ID:          86:DB:03:CF:2C:8E:13:43:D9:D4:42:69:0C:05:95:AF:77:A6:2D:A3

Configuration 19:
    Description: Telia_Finland
    Type:        software
    Size:        107208
    Status:      Inactive
    Version:     0x8012446
    ID:          14:1C:D1:A5:B3:82:B0:88:76:43:26:DC:2B:52:96:5D:17:ED:BD:1C

Configuration 20:
    Description: Telia_Denmark
    Type:        software
    Size:        108744
    Status:      Inactive
    Version:     0x8012486
    ID:          6B:7E:98:85:3B:64:BB:94:CF:10:E5:3B:C0:66:3E:00:BD:DE:6A:C5

Configuration 21:
    Description: Telenor_Sweden_Commercial
    Type:        software
    Size:        108956
    Status:      Inactive
    Version:     0x8014305
    ID:          6C:60:AD:96:58:90:36:F3:6E:63:00:B1:AB:3B:99:3B:D6:FD:E4:77

Configuration 22:
    Description: H3G_UK_Commercial
    Type:        software
    Size:        106264
    Status:      Inactive
    Version:     0x8012A75
    ID:          FD:E7:BF:6E:BF:F6:53:37:49:7B:A0:8E:37:8D:ED:D1:7D:26:D1:B6

Configuration 23:
    Description: H3G_Denmark_Commercial
    Type:        software
    Size:        105944
    Status:      Inactive
    Version:     0x8012A98
    ID:          74:64:58:13:06:F6:6B:20:0A:A2:0B:1B:EB:D2:5A:CB:9E:75:B4:CD

Configuration 24:
    Description: Elisa_Finland
    Type:        software
    Size:        106220
    Status:      Inactive
    Version:     0x8014418
    ID:          5A:A8:80:FD:97:47:95:9B:D1:F4:E5:9C:4B:74:A0:6F:C5:10:B4:AB

Configuration 25:
    Description: Commercial-EE
    Type:        software
    Size:        107940
    Status:      Inactive
    Version:     0x8012220
    ID:          4D:67:BF:E2:C6:F9:E6:90:F9:90:A1:28:2D:AD:2A:FC:BD:CB:44:91
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So which one will give me working VoLTE? I've no idea, but I'll throw a random dice and pick
&lt;code&gt;Norway_Telia_Commercial&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The Telia profile works for my german simcard. But better is &lt;code&gt;Volte_OEM_Lab&lt;/code&gt;.
The Telia profile also sets a specific APN when attaching to the LTE network (even when its
configured on Android for a different one).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;qmicli -d /dev/cdc-wdm0 --pdc-activate-config=software,D8:EF:7F:E2:16:24:38:15:AB:FD:BA:CE:2F:B6:A8:A3:49:30:C4:62
[/dev/cdc-wdm0] Successfully requested config activation
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Great! Now let's take a look into the debug menu reachable via &lt;code&gt;*#*#4636#*#*&lt;/code&gt; and select 'Phone information' and on the
3 dots menu 'IMS Service Status'.&lt;/p&gt;
&lt;p&gt;There you can see:&lt;/p&gt;
&lt;p&gt;&lt;img alt="IMS Registration" src="https://www.openimsd.de/04_activating_android_volte/ims_enabled.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Yay, it works.&lt;/p&gt;</content><category term="blog"/><category term="blog"/></entry><entry><title>Creating QMI traces from Android phones</title><link href="https://www.openimsd.de/qmi-sniffing-with-frida.html" rel="alternate"/><published>2025-06-17T01:23:00+02:00</published><updated>2025-06-17T01:23:00+02:00</updated><author><name>Alexander 'lynxis' Couzens &lt;lynxis@fe80.eu&gt;</name></author><id>tag:www.openimsd.de,2025-06-17:/qmi-sniffing-with-frida.html</id><summary type="html">&lt;p&gt;Creating QMI traces from Android phones with Frida to reverse engeneer Voice over LTE&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;To investigate how the Voice over LTE stack in Qualcomm based phones works, I
have to look into the &lt;a href="https://en.wikipedia.org/wiki/Qualcomm_MSM_Interface"&gt;QMI&lt;/a&gt;
interface. QMI is a structured interface using
&lt;a href="https://en.wikipedia.org/wiki/Type%E2%80%93length%E2%80%93value"&gt;TLV&lt;/a&gt;.
Furthermore, QMI is a protocol which can be used over different interface and
offers communication between services.  A service usually handles a logical
component of the baseband, e.g. the communication with the simcard is handled
by the UIM (User Identity Module).  Or the NAS handles the high level Network
Access.  QMI also supports multiple clients at the same time. It allows two
daemons to communicate via QMI at the same time without the need to synchronize
with each other.&lt;/p&gt;
&lt;p&gt;QMI supports at least communication over the following transports (not a full
list):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;USB (e.g. mini-pcie)&lt;/li&gt;
&lt;li&gt;Shared memory (SoC)&lt;/li&gt;
&lt;li&gt;Socket (kernel socket class, but internally over shared memory)&lt;/li&gt;
&lt;li&gt;Tunneled over Qualcomm Diag&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To research the QMI VoLTE messages of a stock Android, we need to able to
capture those messages.  When I first encountered QMI and researched how the
"legacy" voice calls are working on Qualcomm based phones, I used Qualcomm Diag
for it using the
&lt;a href="https://gitea.osmocom.org/phone-side/osmo-qcdiag"&gt;osmo-qcdiag&lt;/a&gt; project.
Sadly osmo-qcdiag only supports Qualcomm Diag when exposed as TTY on Linux via
&lt;code&gt;/dev/ttyXXXX&lt;/code&gt;, but not (as many other projects by now) directly via USB
without a kernel driver.  The issue with the kernel driver is the limited
support of devices. When enabling Qualcomm Diag for a new phone, it requires a
small patch to add the USB ID to the driver. Other projects like SCAT or
QCSuper can use libusb to directly communicate with the phone.&lt;/p&gt;
&lt;p&gt;Because of this I tried to add QMI sniffing in SCAT, but it didn't work with
the phone.  I didn't receive any messages after subscribing to the QC Diag
messages.&lt;/p&gt;
&lt;p&gt;But there are other methods to get the QMI messages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a) Compile a custom Android image which sniffs all messages&lt;/li&gt;
&lt;li&gt;b) Use a debugger and attach to a running process&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I've noticed &lt;a href="https://frida.re/"&gt;Frida&lt;/a&gt; which was used to capture QMI message
on iPhones, described in
&lt;a href="https://dl.acm.org/doi/fullHtml/10.1145/3678890.3678898"&gt;this paper&lt;/a&gt;.
To summarize: they used Frida, which attaches similar as a debugger to
processes and injects code into the running binary to sniff the QMI messages.&lt;/p&gt;
&lt;p&gt;iPhones communicate in a different way than Android, but I could use the same
approach.  The idea is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use a &lt;strong&gt;rooted&lt;/strong&gt; OnePlus 6T (running Android)&lt;/li&gt;
&lt;li&gt;Use Frida over adb with root privileges&lt;/li&gt;
&lt;li&gt;Export QMI as
  &lt;a href="https://gitea.osmocom.org/peremen/gsmtapv3/src/branch/master/GSMTAPv3.md"&gt;GSMTAPv3&lt;/a&gt;
  (to be able to capture them with tcpdump)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;Frida has two parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A remote server, running on the Android phone&lt;/li&gt;
&lt;li&gt;A client, running on a Laptop (implemented as Python library)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The client will instruct the server over adb to inject a JavaScript engine into
a process, then connect to the JavaScript engine and upload a script.  The
script contains callbacks and code to extract the QMI message, Frida will
transfer those to the client and the client will encapsulate QMI into GSMTAPv3.&lt;/p&gt;
&lt;h2&gt;How to use qmi-frida-tracer&lt;/h2&gt;
&lt;p&gt;Requirement: use a rooted LineageOS on an Android (OnePlus 6T).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;qmi-frida-tracer was developed with Frida 16.6.6!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Download the server from &lt;a href="https://github.com/frida/frida"&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;# ensure adb is able to run as root
adb root

# copy and run Frida server
adb push frida-server-16.6.6-android-arm64 /tmp/
adb shell chmod 700 /tmp/frida-server-16.6.6-android-arm64
# keep the server running
adb shell /tmp/frida-server-16.6.6-android-arm64 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now prepare and run qmi-frida-tracer:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;git&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;clone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="o"&gt;://&lt;/span&gt;&lt;span class="n"&gt;gitlab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;postmarketos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;modem&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;openimsd&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;qmi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;frida&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;tracer&lt;/span&gt;
&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;qmi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;frida&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;tracer&lt;/span&gt;

&lt;span class="c1"&gt;# create a virtualenv and install frida&lt;/span&gt;
&lt;span class="n"&gt;virtualenv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;venv&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;venv&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;activate&lt;/span&gt;
&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;requirements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;

&lt;span class="c1"&gt;# like a `ps`. Show all processes&lt;/span&gt;
&lt;span class="n"&gt;frida&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;U&lt;/span&gt;
&lt;span class="c1"&gt;# use rild pid&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;qmi&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;frida&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9595&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;qmi-frida-tracer is sniffing all QMI messages of a process and encapsulate it
into GSMTAPv3 and transmits it as UDP to &lt;code&gt;localhost:4729&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Capture the QMI traffic as pcap and decode QMI messages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;tcpdump -i lo udp port 4729 -w /tmp/qmi.pcap
# and/or
# compile &amp;amp; run qmi-gsmtap-decode
cd ./qmi-gsmtap-decode/
mkdir build
cd build
cmake .. ; make
./qmi-gsmtap-decode | tee -a qmi.log
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are still some smaller parts open to improve.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simulate a client ID&lt;/li&gt;
&lt;li&gt;Wireshark support for GSMTAPv3 with QMI decoding&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Example captures&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;QMI&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;QRTR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;nas&amp;quot;&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;indication&amp;quot;&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;227&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;tlv_length&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Signal Info&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x0051&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TLV&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;LTE Signal Strength&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;B4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;F6&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;FF&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="nx"&gt;E&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;translated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rssi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;76&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rsrq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rsrp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;112&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;snr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;QMI&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;or&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;QMI&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;QRTR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;wds&amp;quot;&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;none&amp;quot;&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;tlv_length&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Set IP Family&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x004D&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TLV&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Preference&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x01&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;06&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;translated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ipv6&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;QMI&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;QMI&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;QRTR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;wds&amp;quot;&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;response&amp;quot;&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;tlv_length&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Set IP Family&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x004D&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TLV&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Result&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x02&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;
&lt;span class="nx"&gt;QMI&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nx"&gt;translated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SUCCESS&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;QMI&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;More information&lt;/h2&gt;
&lt;p&gt;If you would like to contribute to OpenIMSD or follow its development, see
these links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Matrix: &lt;a href="https://matrix.to/#/#openimsd:postmarketos.org"&gt;#openimsd:postmarketos.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;IRC: &lt;a href="https://webchat.oftc.net/?nick=WebIRC...&amp;amp;prompt=1&amp;amp;uio=OT10cnVlJjExPTIxNSYxMj10cnVlea&amp;amp;channels=#openimsd"&gt;#openimsd&lt;/a&gt; on OFTC&lt;/li&gt;
&lt;/ul&gt;</content><category term="blog"/><category term="blog"/></entry><entry><title>Hello World OpenIMSd</title><link href="https://www.openimsd.de/01-hello-world.html" rel="alternate"/><published>2025-03-02T01:23:00+01:00</published><updated>2025-03-02T01:23:00+01:00</updated><author><name>Alexander 'lynxis' Couzens &lt;lynxis@fe80.eu&gt;</name></author><id>tag:www.openimsd.de,2025-03-02:/01-hello-world.html</id><summary type="html">An introduction to OpenIMSd</summary><content type="html">&lt;div class="paragraph"&gt;
&lt;p&gt;Current free mobile phone OSes (like postmarketOS, &amp;#8230;&amp;#8203;) only support voice calls
for 2G and 3G on Qualcomm based devices.
Calls via 4G using VoLTE are not supported (*except CSFB, which redirects to 2G/3G)
because the modems require a lot of addional configuration and implement
VoLTE with a lot of complexity. This is specially relevant because 2G and 3G
networks are in the process of being shut down all around the world.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Our goal is to support VoLTE on Qualcomm based phones.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We choose Qualcomm based Android phones, because they are the commonly used phones and
widely available. Also Qualcomm baseband has received a lot of reasearch (and reverse engeering)
over the years and a lot of knowledge has been collected in the open.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;To archive our goal we plan to:&lt;/p&gt;
&lt;/div&gt;
&lt;div class="ulist"&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Document the relevant ABIs and add support to libqmi&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Implement a IMS configuration daemon (OpenIMSd) which will communicate with
the baseband using QMI (Qualcomm MSM interface) to configure the relevant services&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Integrate OpenIMSD into FOSS smartphone OS (postmarketOS, &amp;#8230;&amp;#8203;)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;Our implementation will heavily use the baseband in the same way as Android is use the baseband to allow VoLTE. By using the baseband, it should be possible to allow handover between 2G/3G and VoLTE while in a call.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="paragraph"&gt;
&lt;p&gt;We thank NLnet for funding our work (&lt;a href="https://nlnet.nl/project/VoLTE-Qualcom/" class="bare"&gt;https://nlnet.nl/project/VoLTE-Qualcom/&lt;/a&gt;).&lt;/p&gt;
&lt;/div&gt;</content><category term="blog"/><category term="blog"/></entry></feed>