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.

About these ads

Posted on August 20, 2012, in Development, FreeDesktop Planet, Planets and tagged , , . Bookmark the permalink. 15 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 = 0×00
    <<<<<< service = "ctl"
    <<<<<< client = 0
    <<<<<< QMI:
    <<<<<< flags = "none"
    <<<<<< transaction = 1
    <<<<<< tlv_length = 4
    <<<<<< message = "Allocate CID" (0×0022)
    <<<<<< TLV:
    <<<<<< type = "Service" (0×01)
    <<<<<< 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 = 0×80
    >>>>>> service = “ctl”
    >>>>>> client = 0
    >>>>>> QMI:
    >>>>>> flags = “response”
    >>>>>> transaction = 1
    >>>>>> tlv_length = 12
    >>>>>> message = “Allocate CID” (0×0022)
    >>>>>> TLV:
    >>>>>> type = “Result” (0×02)
    >>>>>> length = 4
    >>>>>> value = 00:00:00:00
    >>>>>> translated = SUCCESS
    >>>>>> TLV:
    >>>>>> type = “Allocation Info” (0×01)
    >>>>>> 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 = 0×00
    <<<<<< service = "wds"
    <<<<<< client = 1
    <<<<<< QMI:
    <<<<<< flags = "none"
    <<<<<< transaction = 1
    <<<<<< tlv_length = 17
    <<<<<< message = "Start Network" (0×0020)
    <<<<<< TLV:
    <<<<<< type = "APN" (0×14)
    <<<<<< 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 = 0×80
    >>>>>> service = “wds”
    >>>>>> client = 1
    >>>>>> QMI:
    >>>>>> flags = “response”
    >>>>>> transaction = 1
    >>>>>> tlv_length = 7
    >>>>>> message = “Start Network” (0×0020)
    >>>>>> TLV:
    >>>>>> type = “Result” (0×02)
    >>>>>> 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 = 0×00
    <<<<<< service = "ctl"
    <<<<<< client = 0
    <<<<<< QMI:
    <<<<<< flags = "none"
    <<<<<< transaction = 1
    <<<<<< tlv_length = 4
    <<<<<< message = "Allocate CID" (0×0022)
    <<<<<< TLV:
    <<<<<< type = "Service" (0×01)
    <<<<<< 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 = 0×80
    >>>>>> service = “ctl”
    >>>>>> client = 0
    >>>>>> QMI:
    >>>>>> flags = “response”
    >>>>>> transaction = 1
    >>>>>> tlv_length = 12
    >>>>>> message = “Allocate CID” (0×0022)
    >>>>>> TLV:
    >>>>>> type = “Result” (0×02)
    >>>>>> length = 4
    >>>>>> value = 00:00:00:00
    >>>>>> translated = SUCCESS
    >>>>>> TLV:
    >>>>>> type = “Allocation Info” (0×01)
    >>>>>> 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 = 0×00
    <<<<<< service = "wds"
    <<<<<< client = 1
    <<<<<< QMI:
    <<<<<< flags = "none"
    <<<<<< transaction = 1
    <<<<<< tlv_length = 17
    <<<<<< message = "Start Network" (0×0020)
    <<<<<< TLV:
    <<<<<< type = "APN" (0×14)
    <<<<<< 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 = 0×80
    >>>>>> service = “wds”
    >>>>>> client = 1
    >>>>>> QMI:
    >>>>>> flags = “response”
    >>>>>> transaction = 1
    >>>>>> tlv_length = 7
    >>>>>> message = “Start Network” (0×0020)
    >>>>>> TLV:
    >>>>>> type = “Result” (0×02)
    >>>>>> 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.

  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

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 36 other followers

%d bloggers like this: