Blog Archives

An introduction to libqmi

What is QMI?

QMI is a binary protocol designed to replace the AT command based communication with modems, and is available in devices with Qualcomm chipsets from multiple vendors (Novatel, Huawei, Sierra Wireless, ZTE… and of course Qualcomm itself).

The protocol defines different ‘services‘, each of them related to different actions that may be requested to the modem. For example, the ‘DMS’ (Device Management) service provides actions to load device information; while the ‘NAS’ (Network Access) service provides actions to register in the network. Similarly, other services will allow the user to request data connections (WDS), setup GPS location reporting (PDS), or manage internals of the user identity module (UIM service). The user needs to handle the creation of ‘clients’ for those services by allocating/deallocating ‘client IDs’ using the generic always-on ‘control’ (CTL) service.

Each service in the protocol defines ‘Request and Responses‘ as well as ‘Indications‘. Each pair of request/response has a maching ID which lets the user concatenate multiple requests and get out-of-order responses that can be afterwards matched through the common ID. Indications arrive as unsolicited messages, sent either to a specific client or as a broadcast message to all the clients of a given service. Usually the user needs to request the enabling of the indications to receive via some request/response.

Finally, each message in the protocol defines a set of input (in requests) and output (in responses and indications) arguments, which we name as TLVs. Some of these are defined to be mandatory, others are optional, and others are only mandatory if some other argument is available and has a given value. For example, some output arguments in response messages are only mandatory if the result of the response (given as a TLV) is SUCCESS.


Using the QMI protocol

This protocol is easily accessible in recent enough Linux kernels (>= 3.4), through the cdc-wdm and qmi_wwan drivers. Once these drivers are in place and the modem gets plugged in, the kernel will expose a new /dev/cdc-wdm device which can talk QMI, along with a wwan interface associated to each QMI port.



libqmi is an on-going effort to get a library providing easy access to Qualcomm’s ‘QMI’ protocol. The current libqmi available is based on the GLib/GObject/GIO-powered ‘libqmi-glib’. libqmi tries to ease the use of the protocol by providing:

  • A ‘QmiDevice‘ object to control the access to the /dev/cdc-wdm device. This object allows creating new per-service ‘QmiClient’s. It also hides the use of the implicit ‘CTL’ service client.
  • The ‘QmiClient‘ object provides an interface to the set of request/responses of a given service. Users of the library will just need to execute a GIO-asynchronous method to send the request and get the processed response.
  • The ‘QmiClient‘ object also provides signals for each of the indications given in the service.
  • The input and output arguments needed in requests, responses and indications are compiled into ‘bundles’. These bundles are opaque structs with accessors for their elements, which allow us to add new TLVs to a given message in the future without breaking API/ABI.

The protocol is written in the library as a JSON dictionary of rules, and most of the code to handle the protocol in the library is autogenerated during compilation. If you want to take a look at the currently available interface, check the gtk-doc documentation at:



The libqmi project comes with a command line utility (qmicli) primarily used as a developer tool for testing the libqmi-glib library. Just run the program with ‘--help-all‘ to get all possible actions it can run. A quick example of usage:

$> sudo qmicli -d /dev/cdc-wdm0 --dms-get-manufacturer
[/dev/cdc-wdm0] Device manufacturer retrieved:
Manufacturer: 'Qualcomm Incorporated'

Hint: If you compile the project passing -DMESSAGE_ENABLE_TRACE to CFLAGS, it will dump as DEBUG logs the content and translation of all the QMI messages passed in both directions. You’ll also need ‘--verbose‘ or just ‘-v‘ to show the debug logs when running qmicli.


Other utilities

The project also comes with a small bash script called ‘qmi-network‘, which allows you to launch a broadband connection using libqmi-glib and qmicli. This script requires two arguments; first the cdc-wdm device to talk to, and secondly the action (start|stop|status) to execute. The script can definitely be improved in multiple ways (e.g. send PIN), so if anyone wants to do it patches are very welcome.


Where do I get it?

The project is currently available in the infrastructure:



ModemManager has QMI support through libqmi-glib since release 1.0.

Help! Contribute! Sponsor!

If you want to help, or sponsor further development in libqmi, qmicli or the ModemManager integration, just let me know!

[UPDATE] Links updated, along with ModemManager related info.

Looking for a ModemManager logo

Hey designers!

ModemManager (a.k.a “MM“) is a small daemon which takes care of controlling your broadband devices in your GNU/Linux system. We currently don’t have a logo for ModemManager, so if you’re a designer with some free time, share your ideas! Something simple, similar to the NetworkManager logo would be great, actually.

Enabling GPS location in ModemManager

Location information in ModemManager

Since MM 0.5 at least, ModemManager comes with a Location interface which is defined to expose not only 3GPP-specific location information (operator MMC/MNC + Location area code + Cell ID), but also GPS-specific location information (cached NMEA traces and raw location information). In MM 0.7, this Location interface will actually work for GPS location, at least for Option modems with GPS capabilities.

The HSO plugin

The High-Speed Option plugin is probably one of the most complex plugins being handled in ModemManager. In the new ModemManager 0.7 codebase, HSO modems (MMBroadbandModemHso) are implemented as a subclass of the standard Option modems (MMBroadbandModemOption), as they share lots of common features (allowed modes management, access technology and signal quality reporting…); but they also implement lots of connection related HSO-specific features (e.g. success of a connection establishment gets notified via unsolicited messages), which are most of them handled in a new HSO-specific bearer. Some of these advanced Option modems come with additional surprises: GPS-specific ports (AT port for control, raw serial port for NMEA traces).

The HSO modems provide detailed information about the type of each port being exposed in the USB interface; and so during the probing phase we are able to easily grab the GPS-control AT port and the additional serial port for NMEA traces (handled by a new MMGpsSerialPort port type). Once the HSO modem knows it has these two ports, it will allow to enable or disable any of the two GPS-specific location sources (NMEA or raw), or both at the same time. The NMEA location source will enable the modem to expose the last cached NMEA traces; while the raw location source will tell the modem to just expose generic location information such as time, latitude, longitude and altitude. In the case of the HSO modem (and possibly in every other case), both location sources are actually implemented based on the same NMEA traces received (raw information is parsed from $GTGGA traces). In addition to the core GPS location handling and the HSO-specific implementation, libmm-glib and mmcli where also improved to easily play with the new features.

Trying the GPS location retrieval with your Option modem

If you want to give a quick try to these capabilities, grab ModemManager git master, compile, install and plug in your modem. You first need to check whether the modem has GPS-specific location capabilities. Note that we’ll assume the modem is exposed as index 0; if you have more than one modem, just use --list-modems to check the proper modem index:

$ mmcli -m 0 --location-status
  Location | capabilities: '3gpp-lac-ci, gps-raw, gps-nmea'
           |      enabled: 'none'
           |      signals: 'no'

The output says that the modem supports 3GPP Location area code/Cell ID, GPS raw and GPS-NMEA location sources. None is enabled yet, as we didn’t enable the modem, which we can do issuing:

$ sudo mmcli -m 0 --enable
successfully enabled the modem

$ mmcli -m 0 --location-status
  Location | capabilities: '3gpp-lac-ci, gps-raw, gps-nmea'
           |      enabled: '3gpp-lac-ci'
           |      signals: 'no'

The 3GPP location source will always be enabled by default if it is supported by the modem, while the GPS-specific ones will always be disabled by default. The reasoning behind this is that the 3GPP LAC/CI comes for free in C(G)REG notifications when C(G)REG=2 is supported, while the GPS location is usually an additional module in the modem, with high power consumption. Using mmcli, we can now enable both GPS raw and NMEA location sources:

$ sudo mmcli -m 0 \
             --location-enable-gps-raw \
successfully setup location gathering

If we do check again the status, we’ll see the GPS-specific locations are enabled:

$ mmcli -m 0 --location-status
  Location | capabilities: '3gpp-lac-ci, gps-raw, gps-nmea'
           |      enabled: '3gpp-lac-ci, gps-raw, gps-nmea'
           |      signals: 'no'

Once they are enabled, ModemManager will start receiving NMEA traces in the GPS raw port, and will start to cache and process them. mmcli allows to show location-source-specific information with --location-get-3gpp, --location-get-gps-nmea and --location-get-gps-raw; but it also allows to query for all at the same time:

$ sudo mmcli -m 0 --location-get
  3GPP location   | Mobile country code: '214'
                  | Mobile network code: '3'
                  |  Location area code: '21071'
                  |             Cell ID: '7033737'
  GPS NMEA traces | $GPGGA,,,,,,0,,,,,,,,*66
                  | $GPRMC,,V,,,,,,,,,,N*53
                  | $GPGSA,A,1,,,,,,,,,,,,,,,*1E
                  | $GPGSV,4,1,16,24,,,,29,,,,05,,,,18,,,*7A
                  | $GPGSV,4,2,16,22,,,,14,,,,11,,,,17,,,*7B
                  | $GPGSV,4,3,16,03,,,,12,,,,30,,,,13,,,*78
                  | $GPGSV,4,4,16,23,,,,15,,,,27,,,,07,,,*79
                  | $GPVTG,,T,,M,,N,,K,N*2C
  Raw GPS         | Not available

Note that ModemManager will expose only the last received NMEA traces of each kind, except for those ($GPGSV here) that are actually building a sequence.

Once the GPS module fixes a position, we’ll process the $GPGGA NMEA trace to get valid GPS location information:

$ sudo mmcli -m 0 --location-get-gps-raw
  Raw GPS         |  UTC time: '155142.2'
                  | Longitude: '-3.513941'
                  |  Latitude: '40.502603'
                  |  Altitude: '18.000000'

What about other modems?

Well, if the modem exposes GPS-specific ports in the same USB interface, it shouldn’t be difficult to add support for them, specially now that all the core/libmm-glib/mmcli work is already available.

A special case is when the modem exposes a single AT port via USB and the GPS port via RS232 (e.g. Sierra Wireless FXT009 with GPS extension). This case is a bit more tricky as there is no easy way for ModemManager to tell that the RS232 port is to be managed by the same modem object created after grabbing the AT port. In this case, the user may need to explicitly set some magic udev tags to let ModemManager handle the needed logic, but that also may get tricky if the user has more than one such modem plugged in (not sure for what, but anyway).