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

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: http://www.freedesktop.org/software/libqmi/libqmi-glib/latest/

 

qmicli

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 freedesktop.org infrastructure:

 

ModemManager

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.

Posted on August 20, 2012, in Development, FreeDesktop Planet, Planets and tagged , , . Bookmark the permalink. 35 Comments.

  1. Does libqmi hav a way to notify or throw a signal when qmicli … –wds-get-packet-service-status toggles? (particularly from connected to disconnected).

  2. The QmiClient for the WDS service created by libqmi will get a signal when a Packet Service Status indication arrives; but for the case of qmicli this is useless because the QmiClient is create for each command run. The only case where the QmiClient is kept alive and qmicli running is when using ‘–wds-follow-network’. In this case, we could listen for the Packet Service Status indications and quit the execution when we get disconnected by the network. Open a bug in libqmi if you feel it’s worth having this: https://bugs.freedesktop.org/buglist.cgi?product=libqmi&list_id=312814

  3. hi ,
    Good article . Can you please specify how to build qmicli for MIPS platform
    I am getting errors when i get targer=mytool-chain- .

  4. libqmi should get compiled as any other autotools-based project; I did compile it for MIPS long time ago, using OpenWRT’s toolchain, so unless something got broken recently (possible), you shouldn’t get any issue.

  5. I m trying to comiple with tooldhain than would generate file of type “ELF 32-bit MSB executable, MIPS, MIPS32 rel2 version 1 (SYSV)”

    When I try to make it show error in linking
    LIBQMI_GLIB_LIBS = -lgio-2.0 -lgobject-2.0 -lglib-2.0

    I tried generating the same .so but its not allowing me to cross-compile .

    is there any alternative lib that can be used ?

  6. hi alex ,
    now i am able to successfullly build and get the file of required type.
    It was kinda tricky to cross compile glib .
    I made few changes in my cache file using options of glib .

    and finally its working .

    Thanks for the tool. Keep rocking ..:-)

  7. Glad you fixed the issues:)

  8. I am trying libqmi with e392 its working , but same it is not working e1732

    I get the following error “couldn’t start network: QMI protocol error (10): ‘InvalidProfile’ ”

    root@administrator-OptiPlex-390:~/.codelite/qmilib/app/Debug# qmicli -v -d /dev/cdc-wdm0 –wds-start-network=airtelgprs.com –client-no-release-cid
    [20 Aug 2013, 18:02:20] [Debug] QMI Device at ‘/dev/cdc-wdm0’ ready
    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Assuming service ‘wds’ is supported…
    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Allocating new client ID…
    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Sent message…
    <<<<<< RAW:
    <<<<<< length = 16
    <<<<<< data = 01:0F:00:00:00:00:00:01:22:00:04:00:01:01:00:01

    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Sent message (translated)…
    <<<<<< QMUX:
    <<<<<< length = 15
    <<<<<< flags = 0x00
    <<<<<< service = "ctl"
    <<<<<< client = 0
    <<<<<< QMI:
    <<<<<< flags = "none"
    <<<<<< transaction = 1
    <<<<<< tlv_length = 4
    <<<<<< message = "Allocate CID" (0x0022)
    <<<<<< TLV:
    <<<<<< type = "Service" (0x01)
    <<<<<< length = 1
    <<<<<< value = 01
    <<<<<>>>>> RAW:
    >>>>>> length = 24
    >>>>>> data = 01:17:00:80:00:00:01:01:22:00:0C:00:02:04:00:00:00:00:00:01:02:00:01:01

    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Received message (translated)…
    >>>>>> QMUX:
    >>>>>> length = 23
    >>>>>> flags = 0x80
    >>>>>> service = “ctl”
    >>>>>> client = 0
    >>>>>> QMI:
    >>>>>> flags = “response”
    >>>>>> transaction = 1
    >>>>>> tlv_length = 12
    >>>>>> message = “Allocate CID” (0x0022)
    >>>>>> TLV:
    >>>>>> type = “Result” (0x02)
    >>>>>> length = 4
    >>>>>> value = 00:00:00:00
    >>>>>> translated = SUCCESS
    >>>>>> TLV:
    >>>>>> type = “Allocation Info” (0x01)
    >>>>>> length = 2
    >>>>>> value = 01:01
    >>>>>> translated = [ service = ‘wds’ cid = ‘1’ ]

    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Registered ‘wds’ (version unknown) client with ID ‘1’
    [20 Aug 2013, 18:02:20] [Debug] Asynchronously starting network…
    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Sent message…
    <<<<<< RAW:
    <<<<<< length = 30
    <<<<<< data = 01:1D:00:00:01:01:00:01:00:20:00:11:00:14:0E:00:61:69:72:74:65:6C:67:70:72:73:2E:63:6F:6D

    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Sent message (translated)…
    <<<<<< QMUX:
    <<<<<< length = 29
    <<<<<< flags = 0x00
    <<<<<< service = "wds"
    <<<<<< client = 1
    <<<<<< QMI:
    <<<<<< flags = "none"
    <<<<<< transaction = 1
    <<<<<< tlv_length = 17
    <<<<<< message = "Start Network" (0x0020)
    <<<<<< TLV:
    <<<<<< type = "APN" (0x14)
    <<<<<< length = 14
    <<<<<< value = 61:69:72:74:65:6C:67:70:72:73:2E:63:6F:6D
    <<<<<>>>>> RAW:
    >>>>>> length = 20
    >>>>>> data = 01:13:00:80:01:01:02:01:00:20:00:07:00:02:04:00:01:00:0A:00

    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Received message (translated)…
    >>>>>> QMUX:
    >>>>>> length = 19
    >>>>>> flags = 0x80
    >>>>>> service = “wds”
    >>>>>> client = 1
    >>>>>> QMI:
    >>>>>> flags = “response”
    >>>>>> transaction = 1
    >>>>>> tlv_length = 7
    >>>>>> message = “Start Network” (0x0020)
    >>>>>> TLV:
    >>>>>> type = “Result” (0x02)
    >>>>>> length = 4
    >>>>>> value = 01:00:0A:00
    >>>>>> translated = FAILURE: InvalidProfile

    error: couldn’t start network: root@administrator-OptiPlex-390:~/.codelite/qmilib/app/Debug# qmicli -v -d /dev/cdc-wdm0 –wds-start-network=airtelgprs.com –client-no-release-cid
    [20 Aug 2013, 18:02:20] [Debug] QMI Device at ‘/dev/cdc-wdm0’ ready
    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Assuming service ‘wds’ is supported…
    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Allocating new client ID…
    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Sent message…
    <<<<<< RAW:
    <<<<<< length = 16
    <<<<<< data = 01:0F:00:00:00:00:00:01:22:00:04:00:01:01:00:01

    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Sent message (translated)…
    <<<<<< QMUX:
    <<<<<< length = 15
    <<<<<< flags = 0x00
    <<<<<< service = "ctl"
    <<<<<< client = 0
    <<<<<< QMI:
    <<<<<< flags = "none"
    <<<<<< transaction = 1
    <<<<<< tlv_length = 4
    <<<<<< message = "Allocate CID" (0x0022)
    <<<<<< TLV:
    <<<<<< type = "Service" (0x01)
    <<<<<< length = 1
    <<<<<< value = 01
    <<<<<>>>>> RAW:
    >>>>>> length = 24
    >>>>>> data = 01:17:00:80:00:00:01:01:22:00:0C:00:02:04:00:00:00:00:00:01:02:00:01:01

    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Received message (translated)…
    >>>>>> QMUX:
    >>>>>> length = 23
    >>>>>> flags = 0x80
    >>>>>> service = “ctl”
    >>>>>> client = 0
    >>>>>> QMI:
    >>>>>> flags = “response”
    >>>>>> transaction = 1
    >>>>>> tlv_length = 12
    >>>>>> message = “Allocate CID” (0x0022)
    >>>>>> TLV:
    >>>>>> type = “Result” (0x02)
    >>>>>> length = 4
    >>>>>> value = 00:00:00:00
    >>>>>> translated = SUCCESS
    >>>>>> TLV:
    >>>>>> type = “Allocation Info” (0x01)
    >>>>>> length = 2
    >>>>>> value = 01:01
    >>>>>> translated = [ service = ‘wds’ cid = ‘1’ ]

    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Registered ‘wds’ (version unknown) client with ID ‘1’
    [20 Aug 2013, 18:02:20] [Debug] Asynchronously starting network…
    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Sent message…
    <<<<<< RAW:
    <<<<<< length = 30
    <<<<<< data = 01:1D:00:00:01:01:00:01:00:20:00:11:00:14:0E:00:61:69:72:74:65:6C:67:70:72:73:2E:63:6F:6D

    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Sent message (translated)…
    <<<<<< QMUX:
    <<<<<< length = 29
    <<<<<< flags = 0x00
    <<<<<< service = "wds"
    <<<<<< client = 1
    <<<<<< QMI:
    <<<<<< flags = "none"
    <<<<<< transaction = 1
    <<<<<< tlv_length = 17
    <<<<<< message = "Start Network" (0x0020)
    <<<<<< TLV:
    <<<<<< type = "APN" (0x14)
    <<<<<< length = 14
    <<<<<< value = 61:69:72:74:65:6C:67:70:72:73:2E:63:6F:6D
    <<<<<>>>>> RAW:
    >>>>>> length = 20
    >>>>>> data = 01:13:00:80:01:01:02:01:00:20:00:07:00:02:04:00:01:00:0A:00

    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Received message (translated)…
    >>>>>> QMUX:
    >>>>>> length = 19
    >>>>>> flags = 0x80
    >>>>>> service = “wds”
    >>>>>> client = 1
    >>>>>> QMI:
    >>>>>> flags = “response”
    >>>>>> transaction = 1
    >>>>>> tlv_length = 7
    >>>>>> message = “Start Network” (0x0020)
    >>>>>> TLV:
    >>>>>> type = “Result” (0x02)
    >>>>>> length = 4
    >>>>>> value = 01:00:0A:00
    >>>>>> translated = FAILURE: InvalidProfile

    error: couldn’t start network: QMI protocol error (10): ‘InvalidProfile’
    [/dev/cdc-wdm0] Client ID not released:
    Service: ‘wds’
    CID: ‘1’
    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Unregistered ‘wds’ client with ID ‘1’
    [20 Aug 2013, 18:02:20] [Debug] Client released
    [/dev/cdc-wdm0] Client ID not released:
    Service: ‘wds’
    CID: ‘1’
    [20 Aug 2013, 18:02:20] [Debug] [/dev/cdc-wdm0] Unregistered ‘wds’ client with ID ‘1’
    [20 Aug 2013, 18:02:20] [Debug] Client released

  9. That is weird; “Invalid Profile” errors in the WDS Start Network response are expected only when the request specifies either a “Profile index” or “Profile index 3GPP2” TLV; which we don’t. Another option would be that this specific modem requires to have any of those TLVs in place; which would mean that we need to be able to create a profile ourselves with the input parameters and then select it during the “WDS Start Network” request.

  10. hi aleksander,
    I tried to get profile list and get default settings(3gpp and 3gpp2) both return errors with QMI_ERR_INVALID_PROFILE_TYPE and QMI_ERR_INVALID_PROFILE.

    Then i thought of creating profile using wds service(QMI_WDS_CREATE_PROFILE_REQ
    ) either of the 3gpp and 3gpp2.

    I tried to configure mandatory tlv and two optional tlvs(pdp(IP),apn) to create profile.

    it returned again errors.

    create profile for 3gpp
    01 1f 00 00 01 01 00 02 00 27 00 13 00 01 01 00 00 11 01 00 00 14 08 00 69 6e 74 65 72 6e 65 74

    response
    01 13 00 80 01 01 02 02 00 27 00 07 00 02 04 00 01 00 03 00

    failed with status QMI_ERR_INTERNAL

    if it is 3gpp2
    failed with status QMI_ERR_INVALID_PROFILE_TYPE

    Can i know possible reasons?

  11. No idea; sorry. I’ve never played with WDS profiles myself.

  12. Robert Miller

    Hi aleksander,
    I’m on fedora using linux kernel 3.11 with a sierra wireless mc7354. With the module plugged in and doing a modprobe qmi_wwan and modprobe qcserial I’m not getting the cdc_wdmx listed in my dev directory.. Do you have any advice?
    Thanks,
    RM

  13. MC7354 is relatively new, and although patches to support those are being backported to stable kernels, they may not have reached yours yet. A 3.14 kernel will do the job for sure… Or otherwise, recompile just the newer qmi_wwan driver for your 3.11…

  14. Hi Aleksander,
    I am able to use a lot of the Functions and it’s working great but now I want to get the GPS information. I think, that I have to use the Indications and have to enable the nmea position reporting. But I can’t find how to receive and use an indication. Could you give me a hint, please?

    I enabled the reporting and then I wait a few seconds. After that I tried to use the qmi_client_process_indication() function to receive a indication but this gives me an error.

    Thanks,
    Nico

  15. The sequence to enable GPS and receive NMEA traces via indications is:
    * Enable GPS: qmi_client_pds_set_gps_service_state()
    * Enable auto-tracking: qmi_client_pds_set_auto_tracking_state()
    * Request NMEA via QMI: qmi_client_pds_set_event_report() with “nmea_position_reporting” set to TRUE.
    * Connect to the indications event: g_signal_connect() on the “event-report” signal of the QmiClientPds.
    * Process the QmiIndicationPdsEventReportOutput received for each indication.

    You can see how ModemManager does this in the following source code:
    http://cgit.freedesktop.org/ModemManager/ModemManager/tree/src/mm-broadband-modem-qmi.c#n8227

  16. I will try this, thank you.

  17. Hi Alex,

    Is there any whitepaper document on the cross compilation of libqmi for ARM.

  18. No, there isn’t anything specific. libqmi uses standard autotools compilation setup, so you can likely follow any other online tutorial explaining cross-compilation for ARM.

  19. Thanks for the response.

    Is it the same applicable for libmbim as well?

  20. Love the introduction.

    But a question occurs: is it possible to send SMS via qmicli or libqmi? What could be a possible implementation with the libqmi API in C?

    Cheers

  21. Yes, it’s possible to do that via libqmi, by constructing the specific 3GPP or 3GPP2 PDU yourself and then using the WMS QMI service. ModemManager is a C application that does this using libqmi:
    http://cgit.freedesktop.org/ModemManager/ModemManager/tree/src/mm-sms-qmi.c

    You can then use “mmcli” to trigger SMS operations directly in ModemManager, as in this other blog post:
    https://sigquit.wordpress.com/2012/09/14/sms-goodies-in-modemmanager/

    qmicli doesn’t support this yet, mainly because I didn’t want to include the PDU building code there.

  22. Hey Alex

    After finally figuring out that the wds procedure needs me to keep both CID and PDH to successfully disconnect i immediately noticed your qmi-network script, which works fine now after a small fix: As you said you’re open for improvements, please consider changing source to . (dot) so that other shells work, too, and not just bash.

    sed ‘s/source/./’

    Thanks a lot.

  23. Hey, not sure what version you’re using, but that was changed a long time ago already:
    http://cgit.freedesktop.org/libqmi/commit/utils/qmi-network.in?id=9bcf031b39bcc47da274001bf36fd62618e2a789

  24. Sorry, I expected ubuntu to be less backwards (than debian) about upgrading their packages. Also this showed up first when I googled for the file, it’s also not updated: https://chromium.googlesource.com/chromiumos/third_party/libqmi/+/factory-4455.228.B/utils/qmi-network
    Perhaps google isn’t indexing the freedesktop repo properly or there are the typical ranking problems.
    Thanks that you had updated it anyway:)

    An other thing:
    I see there’s –nas-get-system-selection-preference and –dms-get-band-capabilities functionality. Should the hardware support setting band preferences, too?
    I have managed to limit my modem to LTE with –nas-set-system-selection-preference=lte, but I’d like to know whether it’s possible to specify individual bands, too.

  25. btw, i love the amount of info i can get from –nas-get-cell-location-info
    it’s amazing to watch all this info and triggers the child in me.

    thanks for this:)

  26. Hi Aleksander,
    j’ai installé le driver QMI in my raspberry ( running debian ) et je peux facilement se connecter via la commande : qmi-network /dev/cdc-wdm0 start
    Loading profile…
    APN: internet.ooredoo.tn
    Starting network with ‘qmicli -d /dev/cdc-wdm0 –wds-start-network=internet.ooredoo.tn — client-no-release-cid’…
    Saving state… (CID: 6)
    Saving state… (PDH: 1205860792)
    Network started successfully

  27. Hi Aleksander,
    I installed the driver QMI in my raspberry (running Debian) and I can easily connect via the qmi-network / dev / cdc-start wdm0
    I have added libqmi and I want to write a program to connect to my modem using c and libqmi
    I am Newer with linux, I don’t know how to write a program without an IDE :p

  28. Alex Xin Liu

    Hi Alex
    Is there a alternative solution or to use/compile libqmi under a embedded system compile with uclibc?

  29. I’ve used myself libqmi in a embedded system compiled with uclibc; although if your system is very limited you may prefer to use another solution which doesn’t require GLib/GIO, like uqmi from OpenWRT.

  1. Pingback: Настройка Huawei E392 (Мегафон 4G) на OpenSuSE 12.2

  2. Pingback: An introduction to libmbim « SIGQUIT

  3. Pingback: ModemManager (and latest udev) in OpenWRT « SIGQUIT

  4. Pingback: Sharing a QMI port between processes | SIGQUIT

  5. Pingback: QMI/Gobi management in the kernel: qmi_wwan or GobiNet? | SIGQUIT

  6. Pingback: Using Raspberry Pi as a Carputer and WiFi / 4G Hotspot | Guy Powell's Blog of Geekery

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 109 other followers

%d bloggers like this: