Qualcomm Modem Debugging
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.