Qualcomm Modem Debugging

From postmarketOS Wiki

Qualcomm Modem Debugging - Parse QMI commands from IPC Router Kernel Dump using Libqmi

Qualcomm modems uses QMI commands for controlling the modem. Part of which is already implemented in the libqmi library. In modern qcom SoCs like the SDM845, the IPC Router (downstream) / QRTR (mainline) driver is used as kind of the transport protocol through which the SoC communicates with the modem using QMI commands. Therefore, by basically `printk`-ing the IPC Router driver, we can get the QMI commands that is sent to the modem.

The downstream android IPC Router driver already have logging functionality that can be enabled. However, it only prints the QMI header data and not the entire QMI message. So I wrote a small function that pretty prints these QMI messages in XML format (For a good reason😅, we are gonna use it later).

The downstream kernel side implementation can be found here: https://gitlab.com/sdm845-mainline/android_kernel_xiaomi_sdm845_downstream/-/commits/modem-dump-qmi

Now we can grab the QMI messages we dumped from the dmesg using something like the following:

adb root
adb shell dmesg -t | grep "@QMI@" | sed -e "s/@QMI@//g" &> pocof1_qmi_dump.xml

Here is a sample output of the saved kernel dump:

<qmi svc_id="0x03" type="0" txn_id="30" msg_id="0x0043" android_process="DispatcherModul">
00 30 00 43 00 00 00 00
</qmi>
<qmi svc_id="0x03" type="0" txn_id="2f" msg_id="0x006c" android_process="DispatcherModul">
00 2f 00 6c 00 38 00 11 02 00 14 00 1c 09 00 04
d2 fb fa fb 36 fc 86 fc 1e 09 00 04 82 fb e6 fb
4a fc ae fc 28 09 00 04 00 fb 64 fb c8 fb 2c fc
34 02 00 28 00 36 02 00 3c 00 37 02 00 03 05 00
</qmi>
<qmi svc_id="0x05" type="2" txn_id="0e" msg_id="0x003d" android_process="mpss_IPCRTR">
02 0e 00 3d 00 07 00 02 04 00 00 00 00 00 00 00
</qmi>

Parsing the QMI Kernel Dump using Libqmi

Now that we have nicely formatted dump of the QMI commands, let's take it bit further by using libqmi to parse already implemented QMI commands! And handily libqmi can also "semi"-parse unknown yet valid QMI commands, which can also be useful.

The libqmi parsing functionality is implemented in the following repo: https://gitlab.com/sdm845-mainline/qmi-parse-kernel-dump

Compile the qmi-parse-kernel-dump project and run the following to parse the kernel dump XML file we generated:

parse_qmi_kernel_dump -f pocof1_qmi_dump.xml

The parsed output of the sample qmi commands we dumped in the "previous" section:

x--------------------------------------------------------------------
x--------------------------QMI MESSAGE 1----------------------------
x-----------------Android Process: DispatcherModul------------------------

QMI Service ID     = 0x03
QMI Message ID     = 0x0043
QMI Message Type   = request
QMI Transaction ID = 30

>>>>>> Parsed QMI using Libqmi
<<<<<< QMUX:
<<<<<<   length  = 12
<<<<<<   flags   = 0x00
<<<<<<   service = "nas"
<<<<<<   client  = 1
<<<<<< QMI:
<<<<<<   flags       = "none"
<<<<<<   transaction = 48
<<<<<<   tlv_length  = 0
<<<<<<   message     = "Get Cell Location Info" (0x0043)

x--------------------------------------------------------------------
x--------------------------QMI MESSAGE 2----------------------------
x-----------------Android Process: DispatcherModul------------------------

QMI Service ID     = 0x03
QMI Message ID     = 0x006C
QMI Message Type   = request
QMI Transaction ID = 2f

>>>>>> Parsed QMI using Libqmi
<<<<<< QMUX:
<<<<<<   length  = 68
<<<<<<   flags   = 0x00
<<<<<<   service = "nas"
<<<<<<   client  = 1
<<<<<< QMI:
<<<<<<   flags       = "none"
<<<<<<   transaction = 47
<<<<<<   tlv_length  = 56
<<<<<<   message     = "Config Signal Info v2" (0x006C)
<<<<<< TLV:
<<<<<<   type       = "CDMA RSSI Delta" (0x11)
<<<<<<   length     = 2
<<<<<<   value      = 14:00
<<<<<<   translated = 20
<<<<<< TLV:
<<<<<<   type       = "GSM RSSI Threshold List" (0x1c)
<<<<<<   length     = 9
<<<<<<   value      = 04:D2:FB:FA:FB:36:FC:86:FC
<<<<<<   translated = { [0] = '-1070 ' [1] = '-1030 ' [2] = '-970 ' [3] = '-890 '}
<<<<<< TLV:
<<<<<<   type       = "WCDMA RSSI Threshold List" (0x1e)
<<<<<<   length     = 9
<<<<<<   value      = 04:82:FB:E6:FB:4A:FC:AE:FC
<<<<<<   translated = { [0] = '-1150 ' [1] = '-1050 ' [2] = '-950 ' [3] = '-850 '}
<<<<<< TLV:
<<<<<<   type       = "LTE RSRP Threshold List" (0x28)
<<<<<<   length     = 9
<<<<<<   value      = 04:00:FB:64:FB:C8:FB:2C:FC
<<<<<<   translated = { [0] = '-1280 ' [1] = '-1180 ' [2] = '-1080 ' [3] = '-980 '}
<<<<<< TLV:
<<<<<<   type       = "NR5G SNR Delta" (0x34)
<<<<<<   length     = 2
<<<<<<   value      = 28:00
<<<<<<   translated = 40
<<<<<< TLV:
<<<<<<   type       = "NR5G RSRP Delta" (0x36)
<<<<<<   length     = 2
<<<<<<   value      = 3C:00
<<<<<<   translated = 60
<<<<<< TLV:
<<<<<<   type       = "NR5G Report" (0x37)
<<<<<<   length     = 2
<<<<<<   value      = 03:05
<<<<<<   translated = [ rate = '3' average_period = '5' ]

x--------------------------------------------------------------------
x--------------------------QMI MESSAGE 3----------------------------
x-----------------Android Process: mpss_IPCRTR------------------------

QMI Service ID     = 0x05
QMI Message ID     = 0x003D
QMI Message Type   = response
QMI Transaction ID = 0e

>>>>>> Parsed QMI using Libqmi
<<<<<< QMUX:
<<<<<<   length  = 19
<<<<<<   flags   = 0x00
<<<<<<   service = "wms"
<<<<<<   client  = 1
<<<<<< QMI:
<<<<<<   flags       = "response"
<<<<<<   transaction = 14
<<<<<<   tlv_length  = 7
<<<<<<   message     = (0x003d)
<<<<<< TLV:
<<<<<<   type   = 0x02
<<<<<<   length = 4
<<<<<<   value  = 00:00:00:00

Discussion

As we can see from the above example, using libqmi to parse the known QMI commands is very useful in understanding the QMI messages. We can note that the 3rd command is not actually implemented, but libqmi is able to semi-parse it. Although libqmi doesn't exactly know what message is this, what the TLVs/variables mean, it is still able to provide information on what the QMI service (WMS - Wireless Message Service) is, the QMI message id is 0x003d, there is only single TLV of length 4 and the TLVs value.

The sad part, while we know the QMI messages and it's structure, we can't just go ahead and implement it without knowing what each TLV means and what the values represent. And since QMI protocal is Qualcomm's proprietary protocol, it's bit hard to find the specification of all of the missing QMI messages.

So what's the use of all this..............?

Mainly, understanding the flow of modem communication and figuring out what's missing. This work started as we were trying to understand what's missing to implement VoLTE functionality in ModemManager (https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/378). Now we can compare ModemManager debug logs and Android QMI logs and how the QMI message flows for each functionality. Like what are the QMI messages involved when enabling mobile data, when enabling VoLTE, etc and eventually fill the gaps in ModemManager.

Once we know what we are missing, we can try to guess the parts of the missing QMI commands. Qualcomm is not very strict when it comes to implementing these commands. They just don't want to officially dump their documentation on the internet. Recently engineers from Qualcomm and Google with access to QMI documentation contribute directly to ModemManager and libqmi. In my understanding, some developers have access to the QMI documentation under NDA agreements. So currently our best bet would be, once we document the process and note the needed QMI commands, we can probably open up a discussion in the ModemManager project.