Merge cros/upstream to cros/master

Contains the following commits:
 0a119a7 libmbim-glib,device: ensure CLOSED state always in mbim_device_close() (Aleksander Morgado)
 ce8b4da libmbim-glib,device: don't allow close() if opening (Aleksander Morgado)
 411287f mbim-codegen: add support to r/w 16bit unsigned integers (Som_SP)
 9f9da41 libmbim-glib,ms-basic-connect-extensions: adding cid 'version' (Som_SP)
 f58ba1c libmbim-glib,ms-basic-connect-extensions: adding cid 'location-info-status' (Som_SP)
 5a8672e mbimcli,ms-basic-connect-extensions: remove redundant comments (Aleksander Morgado)
 8af3bbd mbimcli,ms-basic-connect-extensions: minor comment style issues (Aleksander Morgado)
 87413ab mbimcli,ms-basic-connect-extensions: improve --ms-query-version command help (Aleksander Morgado)
 a596ba4 mbimcli,ms-basic-connect-extensions: switch 16 bit reader to bcd reader (Aleksander Morgado)
 a64d93a build: use python3 everywhere (goapunk)
 f5a06ff build: Use the newer python module (Iñigo Martínez)
 28dc349 build: Make use of the summary function (Iñigo Martínez)
 2e6edfc build: Create a variable for the project name (Iñigo Martínez)
 69c72d3 build: Use / instead of join_paths (Iñigo Martínez)
 38deb2f build: Removed duplicated variable (Iñigo Martínez)
 6b4a303 build: Make use of features build option for introspection (Iñigo Martínez)
 9607e04 build: Make use of dictionaries (Iñigo Martínez)
 28674e6 build: Use the if_found feature (Iñigo Martínez)
 02b027b build: Use strings for include_directories (Iñigo Martínez)
 35abb03 build: Use the install_mode option (Iñigo Martínez)
 86a5dc7 build: Use install_dir instead of subdir (Iñigo Martínez)
 531374d build: Add version script linker flag when available (Iñigo Martínez)
 433108b build: Remove unnecessary MALLOC_PERTURB_ variable set (Iñigo Martínez)
 231ade7 build: Add explicit content files to documentation (Iñigo Martínez)
 5da217a libmbim-glib,mbimcli: remove execution bit from mode in source files (Aleksander Morgado)
 d356118 build: cleanup gitignore after meson port (Aleksander Morgado)
 9e0a74d ci: add tests without introspection support (Aleksander Morgado)
 73137ab ci: add uninstall check in the release task (Aleksander Morgado)
 8fe6a3d libmbim-glib,device: new 'MS_MBIMEX_V2' open flag (Aleksander Morgado)
 547de0c libmbim-glib,device: new methods to manage the MBIMEx version (Aleksander Morgado)
 418f289 libmbim-glib,enums: define 5G NSA/SA data classes (Aleksander Morgado)
 18c5e77 mbim-codegen: no need to redefine service inside each command (Aleksander Morgado)
 ae83b73 ms-basic-connect-v2: new 'MS Basic Connect V2" service and 'Register State' message (Aleksander Morgado)
 edb1d92 libmbim-glib,message: new method to get printable string with mbimex support (Aleksander Morgado)
 2842748 mbimcli: allow requesting MBIMEx v2.0 support on device open (Aleksander Morgado)
 63cf334 mbimcli,basic-connect: support MBIMEx v2.0 'Register State' (Aleksander Morgado)
 39abda6 libmbim-glib,proxy: monitor MBIMEx version exchange and track per device (Aleksander Morgado)
 d94fb7a libmbim-glib,proxy: notify to all clients updates in the MBIMEx version (Aleksander Morgado)
 baa6641 libmbim,device: receive from proxy MBIMEx version updates (Aleksander Morgado)
 67f895a libmbim-glib,proxy: notify to new clients the current MBIMEx version (Aleksander Morgado)
 d21bcb6 ms-basic-connect-v2: implement 'Packet Service' (Som_SP)
 af6f897 mbimcli,basic-connect: support MBIMEx v2.0 'Packet Service' (Aleksander Morgado)
 718e6ce mbim-codegen: report UNSUPPORTED error when printing unknown CID (Aleksander Morgado)
 1d0415b libmbim-glib,message: fallback to printing v1 format if message undefined in v2 (Aleksander Morgado)
 9538b9f build-aux,codegen: add missing annotations in the input 'ref-struct-array' types (Aleksander Morgado)
 de75176 build-aux,codegen: separate methods to process different struct array types (Aleksander Morgado)
 8dde6bb build-aux,codegen: fix error messages when 'struct-type' field missing (Aleksander Morgado)
 cd3554c build-aux,codegen: define new 'ms-struct-array' type (Aleksander Morgado)
 bd89b91 ms-basic-connect-v2: implement 'Signal State v2' (Aleksander Morgado)
 54d738b sms: fix public format for SMS CDMA language (Aleksander Morgado)
 fa06ce0 build-aux,codegen: document public format type in struct fields (Aleksander Morgado)
 f73fba3 build-aux,codegen: include printable info for struct fields with public type (Aleksander Morgado)
 54cec41 mbimcli,basic-connect: support MBIMEx v2.0 'Signal State' (Aleksander Morgado)
 7ebff59 ms-basic-connect-extensions: implement 'provisioned-context-v2' (Som_SP)
 d6d0114 ms-basic-connect-extensions: minor alignment fixes in JSON (Aleksander Morgado)
 392e95a ms-basic-connect-extensions: rename enum types in 'Provisioned Contexts v2' (Aleksander Morgado)
 caa3b47 docs: add missing references to 'Provisioned Contexts v2' enum types (Aleksander Morgado)
 dd14741 mbimcli,ms-basic-connect-extensions: minor coding style updates (Aleksander Morgado)
 19cd976 mbimcli: don't include possible enum values in the short command help (Aleksander Morgado)
 b35422e mbimcli,basic-connect: overwrite duplicated key-value pairs (Aleksander Morgado)
 d8cb4b0 mbimcli,ms-basic-connect-extensions: use key-value pairs for --ms-set-provisioned-contexts (Aleksander Morgado)
 172c462 mbimcli,basic-connect: deprecate 'apn' key and prefer 'access-string' (Aleksander Morgado)
 6c2d10e mbimcli,basic-connect: minor coding style updates (Aleksander Morgado)
 e7654f8 mbimcli,basic-connect: auth, username and protocol validation only in legacy input format (Aleksander Morgado)
 07ff953 mbimcli: setup common helper to get enum values from strings (Aleksander Morgado)
 c896f5f mbimcli,ms-basic-connect-extensions: avoid custom string to enums parsers (Aleksander Morgado)
 005f1e9 mbimcli,basic-connect: avoid custom string to enums parsers (Aleksander Morgado)
 804f07c mbimcli,basic-connect: support 'compression' in --connect (Aleksander Morgado)
 9808f46 mbimcli,basic-connect: support 'context-type' in --connect (Aleksander Morgado)
 5b6a4ad mbimcli,ms-basic-connect-extensions: fix double message unref (Aleksander Morgado)
 06312c0 mbimcli,sar: use 'all' to set antenna index as '0xFFFFFFFF' (Jack Song)
 64f52fc mbimcli,sar: avoid custom string to enums parsers (Aleksander Morgado)
 7fd0ac2 mbimcli,sar: use regex matching for the list of SAR config states (Aleksander Morgado)
 c9672ae mbimcli,sar: minor changes in how SAR config response is printed (Aleksander Morgado)
 6d2140a mbimcli,sar: print 'all' instead of '0xFFFFFFFF' in SAR config response (Aleksander Morgado)
 80bcf58 mbimcli,sar: demote message to debug level (Aleksander Morgado)
 d1d75f0 ci: bump image template to refresh CA certificates (Aleksander Morgado)
 d9552bc MBIMEx 3.0: implement "registration-params" (Som_SP)
 ef95079 ms-basic-connect-extensions: add comment about unnamed IEs in Register Params (Aleksander Morgado)
 6bfe3e5 ms-basic-connect-extensions: rename 'Registration Params' to 'Registration Parameters' (Aleksander Morgado)
 fa93547 libmbim-glib,cid: add missing entry for 'Registration Parameters' (Aleksander Morgado)
 2ed143c build-aux,codegen: allow 'gboolean' as public output (Aleksander Morgado)
 ae85b33 ms-basic-connect-extensions: rework enum fields in 'Registration Parameters' (Aleksander Morgado)
 03335a2 mbimcli,ms-basic-connect-extensions: use key-value pairs for --ms-set-registration-parameters (Aleksander Morgado)
 21b5c67 docs,reference: add 1.28 index reference (Aleksander Morgado)
 881f9a3 docs,reference: add missing enum documentation (Aleksander Morgado)
 3d44393 libmbim-glib,proxy: report MBIMEx to clients on proxy config (Aleksander Morgado)
 6928485 ms-basic-connect-v3: implement "subscriber-ready-status" (Som_SP)
 48d1a88 libmbim-glib,device: allow requesting MBIMEx v3.0 (Aleksander Morgado)
 a81e655 libmbim-glib,ms-basic-connect-v3: add new service to build (Aleksander Morgado)
 6eff8a7 libmbim-glib,ms-basic-connect-v3: fix ready status flags (Aleksander Morgado)
 c9572ba docs,reference: add missing MbimSubscriberReadyStatusFlag flag docs (Aleksander Morgado)
 7b6b249 mbimcli: allow requesting MBIMEx v3.0 (Aleksander Morgado)
 a710f74 libmbim-glib,message: support printing basic connect v3 messages (Aleksander Morgado)
 60f779c ms-basic-connect-extensions-v3: implement 'lte-attach-info' (Som_SP)
 7a764b4 libmbim-glib,ms-basic-connect-extensions-v3: add new service to build (Aleksander Morgado)
 7ef442b ms-basic-connect-extensions-v3: fix order of NwError field (Aleksander Morgado)
 bb1d6da mbimcli,ms-basic-connect-extensions: print detailed network error info (Aleksander Morgado)
 75f3b0b libmbim-glib,enums: rename MBIM_NW_ERROR_UNKNOWN to MBIM_NW_ERROR_NONE (Aleksander Morgado)
 6378f61 build: version bump to indicate existence of new API (MBIM_NW_ERROR_NONE) (Aleksander Morgado)
 0d31108 build-aux,codegen: always initialize output array size in ms-struct-array (Aleksander Morgado)
 ff2930b build-aux,codegen: treat empty ms-struct-array as no array (Aleksander Morgado)
 f200daf build-aux,codegen: new 'ms-struct' type (Aleksander Morgado)
 75c5b76 build-aux,codegen: support 'gint32' fields in structs (Aleksander Morgado)
 a8673ea libmbim-glib: implement 'Base Stations Info' (Aleksander Morgado)
 426a135 build-aux,codegen: generate support for all versions of same service together (Aleksander Morgado)
 45523ca ms-basic-connect-v2: don't reimplement commands if they didn't change (Aleksander Morgado)
 91adfb1 ms-basic-connect-v3: don't reimplement commands if they didn't change (Aleksander Morgado)
 c3c7277 ms-basic-connect-extensions-v3: don't reimplement commands if they didn't change (Aleksander Morgado)
 0a5176f libmbim-glib: implement 'Base Stations Info v3' (Aleksander Morgado)
 11ba859 MBIMEx 3.0: implement "packet service v3" for SA (Som_SP)
 0ab3098 mbimcli,basic-connect: minor rework on how packet service status response is printed (Aleksander Morgado)
 01b8115 ms-basic-connect-v3: fix MbimTai struct definition (Aleksander Morgado)
 8c781c8 ms-basic-connect: implement connect for mbim version 3 (Som_SP)
 56983e6 libmbim-glib: new MbimTlv generic type (Aleksander Morgado)
 f20b157 libmbim-glib,test,message-builder: include printable output in traces (Aleksander Morgado)
 2bb3cc6 libmbim-glib,test,message-parser: include printable output in traces (Aleksander Morgado)
 9506fd8 ms-basic-connect-v3: switch 'Basic Connect' to use MbimTlvs (Aleksander Morgado)
 b7432fc build-aux,codegen: remove 'string-tlv' from structs (Aleksander Morgado)
 9f60c77 mbimcli,basic-connect: avoid updating connect positional arguments logic (Aleksander Morgado)
 b72136e build-aux,mbim-codegen: report ms-struct and ms-struct-array as nullable (Aleksander Morgado)
 421ead1 libmbim-glib,mbimcli: 'Version' command is part of MBIMEx v2.0 (Aleksander Morgado)
 3ec0440 libmbim-glib,tlv: specify it's used since MBIMEx v3.0 (Aleksander Morgado)
 2658b0b libmbim-glib,mbimcli: 'Registration Parameters' command is part of MBIMEx v3.0 (Aleksander Morgado)
 31bcbea libmbim-glib,enums: add missing Since tags (Aleksander Morgado)
 700d07a libmbim-glib,enums: add comment about MBimDataClass vs MbimDataClassV2 (Aleksander Morgado)
 b292716 libmbim-glib: Adding esim no profile support in ready state (Som_SP)
 ab20894 libmbim-glib,enums: update MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE doc (Aleksander Morgado)
 8482cdc build: version bump to flag existence of new API (NO_ESIM_PROFILE) (Aleksander Morgado)
 ab218de libmbim-glib,enums: minor fix in MBIM_FREQUENCY_RANGE_UNKNOWN (Aleksander Morgado)
 a58bb37 libmbim-glib,proxy: fix wrong message unref in MBIMEx reporting logic (Aleksander Morgado)
 b840b84 docs,reference: add missing entries for mbim_tlv_string_{new|get} (Aleksander Morgado)
 32034e4 libmbim-glib,tlv: fix string reading in BE systems (Aleksander Morgado)
 872259e libmbim-glib,message: remove obsolete read_string_tlv() method (Aleksander Morgado)
 d69bab7 build-aux,codegen: fix attempting to unref a NULL MbimTlv (Aleksander Morgado)
 6d09092 libmbim-glib,message: printable support for MBIMEx v2.0 extensions (Aleksander Morgado)
 96d9140 libmbim-glib,enums: rename 'MbimDataClassV2' to 'MbimDataClassV3' (Aleksander Morgado)
 a5fbc67 libmbim-glib,enums: update MbimCtrlCaps in MBIMEx v3.0 (Aleksander Morgado)
 25b17dc build-aux,codegen: new 'tlv-guint16-array' field type (Aleksander Morgado)
 88cc6b2 MBIMEx 3.0: implement 'device caps v3' (Som_SP)
 5b20317 mbimcli: add support for MBIMEx v3.0 Device Caps (Aleksander Morgado)
 9eb738c ms-basic-connect-extensions-v3: implement 'Modem Configuration' (Som_SP)
 ee9eea6 ms-basic-connect-extensions: implement wake-reason (Som_SP)
 e980523 libmbim-glib,tlv: parsers for wake command and wake packet types (Aleksander Morgado)
 70ccf75 libmbim-glib,test: always enable message traces (Aleksander Morgado)
 e335b17 libmbim-glib,test: add Device Caps v3 parser test (Aleksander Morgado)
 7590e34 libmbim-glib,test: minor refactoring in method names (Aleksander Morgado)
 f6fbc6c build: Fix double quotes in rules file (Iñigo Martínez)
 792af73 meson.build: add an option to not build man pages. (Nicolas Cavallari)
 b72257f basic-connect: implement ip-packet-filters (Som_SP)
 489f40e mbimcli: implement --set-ip-packet-filters (Aleksander Morgado)
 4ea5995 libmbim-glib,uuid: new context type definitions in Microsoft Extensions (Aleksander Morgado)
 8c2eac7 libmbim-glib,uuid: set missing Since tag in MBIM_UUID_MS_UICC_LOW_LEVEL_ACCESS (Aleksander Morgado)
 984fe27 libmbim-glib,enums: fix MbimAccessMediaType documentation (Aleksander Morgado)
 b3cca0c libmbim-glib,api: rename 'network blacklist' to 'network denylist' (Aleksander Morgado)
 523da17 libmbim-glib,device: rename 'dummy' with 'placeholder' (Aleksander Morgado)
 0af01cf Revert "CHROMIUM: Disable help2man doc generation" (Madhav)
 cc3350c Merge branch 'upstream' of https://chromium.googlesource.com/chromiumos/third_party/libmbim into merge-upstream (Madhav)

BUG=b:204815284
TEST=emerge-dedede libmbim && cros deploy && run tast

Cq-Depend: chromium:3290496
Change-Id: Idb5fdac277ea0a545565b54d723530c7b68fab52
diff --git a/.gitignore b/.gitignore
index bdc0a38..98af0c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,82 +1,3 @@
 *~
-*.o
-*.lo
-*.la
 *.bz2
 *.pyc
-*.gir
-*.typelib
-
-Makefile
-Makefile.in
-.deps
-.libs
-
-/INSTALL
-/aclocal.m4
-/autom4te.cache
-/compile
-/config.*
-/configure
-/install-sh
-/libtool
-/ltmain.sh
-/m4
-/missing
-/stamp-h1
-/depcomp
-
-/data/pkg-config/mbim-glib.pc
-
-/docs/reference/libmbim-glib/version.xml
-/docs/reference/libmbim-glib/libmbim-glib.args
-/docs/reference/libmbim-glib/libmbim-glib.hierarchy
-/docs/reference/libmbim-glib/libmbim-glib.interfaces
-/docs/reference/libmbim-glib/libmbim-glib.prerequisites
-/docs/reference/libmbim-glib/libmbim-glib.signals
-/docs/reference/libmbim-glib/libmbim-glib.actions
-/docs/reference/libmbim-glib/libmbim-glib.types
-/docs/reference/libmbim-glib/*.mstamp
-/docs/reference/libmbim-glib/*.stamp
-/docs/reference/libmbim-glib/*.txt
-/docs/reference/libmbim-glib/*.bak
-/docs/reference/libmbim-glib/html
-/docs/reference/libmbim-glib/tmpl
-/docs/reference/libmbim-glib/xml
-/docs/reference/libmbim-glib/.libs
-
-/docs/man/mbimcli.1
-/docs/man/mbim-network.1
-
-/src/common/test/test-common
-
-/src/libmbim-glib/.deps
-/src/libmbim-glib/.libs
-/src/libmbim-glib/mbim-version.h
-
-/src/libmbim-glib/test/.deps
-/src/libmbim-glib/test/.libs
-/src/libmbim-glib/test/test-uuid
-/src/libmbim-glib/test/test-cid
-/src/libmbim-glib/test/test-message
-/src/libmbim-glib/test/test-fragment
-/src/libmbim-glib/test/test-message-parser
-/src/libmbim-glib/test/test-message-builder
-/src/libmbim-glib/test/test-proxy-helpers
-/src/libmbim-glib/test/*.log
-/src/libmbim-glib/test/*.trs
-
-/src/libmbim-glib/generated/.deps
-/src/libmbim-glib/generated/.libs
-/src/libmbim-glib/generated/*.c
-/src/libmbim-glib/generated/*.h
-/src/libmbim-glib/generated/*.sections
-
-/src/mbimcli/.deps
-/src/mbimcli/.libs
-/src/mbimcli/mbimcli
-
-/utils/mbim-network
-
-/src/mbim-proxy/mbim-proxy
-/src/mbim-proxy/76-mbim-proxy-device-ownership.rules
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 68cfbff..0374b49 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -11,8 +11,8 @@
 .common_variables:
   variables:
     FDO_UPSTREAM_REPO: mobile-broadband/libmbim
-    FDO_DISTRIBUTION_VERSION: '18.04'
-    FDO_DISTRIBUTION_TAG: '2021-07-27.1'
+    FDO_DISTRIBUTION_VERSION: '20.04'
+    FDO_DISTRIBUTION_TAG: '2021-10-03.1'
     FDO_DISTRIBUTION_PACKAGES: ca-certificates git gcc libgirepository1.0-dev
                                libglib2.0-dev gtk-doc-tools libglib2.0-doc
                                gobject-introspection bash-completion valac
@@ -30,7 +30,7 @@
     - tags
     - pushes
 
-build-meson-default:
+build-no-introspection:
   stage: build
   extends:
   - .fdo.distribution-image@ubuntu
@@ -41,10 +41,38 @@
     - tags
     - schedules
   script:
-    - meson setup _debug_build --prefix=/usr -Dwerror=true
-    - ninja -C _debug_build
-    - ninja -C _debug_build install
-    - meson setup _release_build --prefix=/usr --buildtype=release -Dwerror=true
-    - ninja -C _release_build
-    - ninja -C _release_build install
-    - ninja -C _release_build dist
+    - meson setup build --prefix=/usr -Dwerror=true -Dintrospection=disabled
+    - ninja -C build
+    - ninja -C build install
+
+build-introspection:
+  stage: build
+  extends:
+  - .fdo.distribution-image@ubuntu
+  - .common_variables
+  only:
+    - master
+    - merge_requests
+    - tags
+    - schedules
+  script:
+    - meson setup build --prefix=/usr -Dwerror=true -Dintrospection=enabled
+    - ninja -C build
+    - ninja -C build install
+
+build-release:
+  stage: build
+  extends:
+  - .fdo.distribution-image@ubuntu
+  - .common_variables
+  only:
+    - master
+    - merge_requests
+    - tags
+    - schedules
+  script:
+    - meson setup build --buildtype=release --prefix=/usr -Dwerror=true -Dintrospection=enabled
+    - ninja -C build
+    - ninja -C build install
+    - ninja -C build dist
+    - ninja -C build uninstall
diff --git a/build-aux/mbim-codegen/Message.py b/build-aux/mbim-codegen/Message.py
index 0d10ca9..0e24cac 100644
--- a/build-aux/mbim-codegen/Message.py
+++ b/build-aux/mbim-codegen/Message.py
@@ -45,6 +45,8 @@
             pass
         elif field['format'] == 'uuid':
             pass
+        elif field['format'] == 'guint16':
+            pass
         elif field['format'] == 'guint32':
             pass
         elif field['format'] == 'guint32-array':
@@ -58,14 +60,20 @@
         elif field['format'] == 'struct':
             if 'struct-type' not in field:
                 raise ValueError('Field type \'struct\' requires \'struct-type\' field')
+        elif field['format'] == 'ms-struct':
+            if 'struct-type' not in field:
+                raise ValueError('Field type \'ms-struct\' requires \'struct-type\' field')
         elif field['format'] == 'struct-array':
             flag_always_read_field(fields, field['array-size-field'])
             if 'struct-type' not in field:
-                raise ValueError('Field type \'struct\' requires \'struct-type\' field')
+                raise ValueError('Field type \'struct-array\' requires \'struct-type\' field')
         elif field['format'] == 'ref-struct-array':
             flag_always_read_field(fields, field['array-size-field'])
             if 'struct-type' not in field:
-                raise ValueError('Field type \'struct\' requires \'struct-type\' field')
+                raise ValueError('Field type \'ref-struct-array\' requires \'struct-type\' field')
+        elif field['format'] == 'ms-struct-array':
+            if 'struct-type' not in field:
+                raise ValueError('Field type \'ms-struct-array\' requires \'struct-type\' field')
         elif field['format'] == 'ipv4':
             pass
         elif field['format'] == 'ref-ipv4':
@@ -78,6 +86,14 @@
             pass
         elif field['format'] == 'ipv6-array':
             flag_always_read_field(fields, field['array-size-field'])
+        elif field['format'] == 'tlv':
+            pass
+        elif field['format'] == 'tlv-string':
+            pass
+        elif field['format'] == 'tlv-guint16-array':
+            pass
+        elif field['format'] == 'tlv-list':
+            pass
         else:
             raise ValueError('Cannot handle field type \'%s\'' % field['format'])
 
@@ -90,9 +106,11 @@
     """
     Constructor
     """
-    def __init__(self, dictionary):
+    def __init__(self, service, mbimex_service, mbimex_version, dictionary):
         # The message service, e.g. "Basic Connect"
-        self.service = dictionary['service']
+        self.service = service
+        self.mbimex_service = mbimex_service
+        self.mbimex_version = mbimex_version
 
         # The name of the specific message, e.g. "Something"
         self.name = dictionary['name']
@@ -154,8 +172,18 @@
         else:
             self.fullname = 'MBIM Message ' + self.service + ' ' + self.name
 
+        # Build SERVICE enum
+        if self.mbimex_service:
+            self.service_enum_name = 'MBIM Service ' + self.mbimex_service
+        else:
+            self.service_enum_name = 'MBIM Service ' + self.service
+        self.service_enum_name = utils.build_underscore_name(self.service_enum_name).upper()
+
         # Build CID enum
-        self.cid_enum_name = 'MBIM CID ' + self.service
+        if self.mbimex_service:
+            self.cid_enum_name = 'MBIM CID ' + self.mbimex_service
+        else:
+            self.cid_enum_name = 'MBIM CID ' + self.service
         if self.name != "":
             self.cid_enum_name += (' ' + self.name)
         self.cid_enum_name = utils.build_underscore_name(self.cid_enum_name).upper()
@@ -194,14 +222,14 @@
     Emit message creator
     """
     def _emit_message_creator(self, hfile, cfile, message_type, fields, since):
-        translations = { 'message'                  : self.name,
-                         'service'                  : self.service,
-                         'since'                    : since,
-                         'underscore'               : utils.build_underscore_name (self.fullname),
-                         'message_type'             : message_type,
-                         'message_type_upper'       : message_type.upper(),
-                         'service_underscore_upper' : utils.build_underscore_name (self.service).upper(),
-                         'cid_enum_name'            : self.cid_enum_name }
+        translations = { 'message'            : self.name,
+                         'service'            : self.service,
+                         'since'              : since,
+                         'underscore'         : utils.build_underscore_name (self.fullname),
+                         'message_type'       : message_type,
+                         'message_type_upper' : message_type.upper(),
+                         'service_enum_name'  : self.service_enum_name,
+                         'cid_enum_name'      : self.cid_enum_name }
 
         template = (
             '\n'
@@ -226,6 +254,8 @@
                                   ' * @${field}: (in)(element-type guint8)(array length=${field}_size): the \'${name}\' field, given as an array of #guint8 values.\n')
             elif field['format'] == 'uuid':
                 inner_template = (' * @${field}: (in): the \'${name}\' field, given as a #MbimUuid.\n')
+            elif field['format'] == 'guint16':
+                inner_template = (' * @${field}: (in): the \'${name}\' field, given as a #${public}.\n')
             elif field['format'] == 'guint32':
                 inner_template = (' * @${field}: (in): the \'${name}\' field, given as a #${public}.\n')
             elif field['format'] == 'guint64':
@@ -236,10 +266,14 @@
                 inner_template = (' * @${field}: (in)(type GStrv): the \'${name}\' field, given as an array of strings.\n')
             elif field['format'] == 'struct':
                 inner_template = (' * @${field}: (in): the \'${name}\' field, given as a #${struct}.\n')
+            elif field['format'] == 'ms-struct':
+                raise ValueError('type \'ms-struct\' unsupported as input')
             elif field['format'] == 'struct-array':
                 inner_template = (' * @${field}: (in)(array zero-terminated=1)(element-type ${struct}): the \'${name}\' field, given as an array of #${struct} items.\n')
             elif field['format'] == 'ref-struct-array':
-                inner_template = (' * @${field}: (in): the \'${name}\' field, given as an array of #${struct} items.\n')
+                inner_template = (' * @${field}: (in)(array zero-terminated=1)(element-type ${struct}): the \'${name}\' field, given as an array of #${struct} items.\n')
+            elif field['format'] == 'ms-struct-array':
+                raise ValueError('type \'ms-struct-array\' unsupported as input')
             elif field['format'] == 'ipv4':
                 inner_template = (' * @${field}: (in): the \'${name}\' field, given as a #MbimIPv4.\n')
             elif field['format'] == 'ref-ipv4':
@@ -252,6 +286,14 @@
                 inner_template = (' * @${field}: (in): the \'${name}\' field, given as a #MbimIPv6.\n')
             elif field['format'] == 'ipv6-array':
                 inner_template = (' * @${field}: (in)(array zero-terminated=1)(element-type MbimIPv6): the \'${name}\' field, given as an array of #MbimIPv6 items.\n')
+            elif field['format'] == 'tlv':
+                inner_template = (' * @${field}: (in)(transfer none): the \'${name}\' field, given as a #${struct} item.\n')
+            elif field['format'] == 'tlv-string':
+                inner_template = (' * @${field}: (in): the \'${name}\' field, given as a string.\n')
+            elif field['format'] == 'tlv-guint16-array':
+                raise ValueError('type \'tlv-guint16-array\' unsupported as input')
+            elif field['format'] == 'tlv-list':
+                inner_template = (' * @${field}: (in)(element-type MbimTlv)(transfer none): the \'${name}\' field, given as a list of #${struct} items.\n')
 
             template += (string.Template(inner_template).substitute(translations))
 
@@ -282,6 +324,8 @@
                                   '    const guint8 *${field},\n')
             elif field['format'] == 'uuid':
                 inner_template = ('    const MbimUuid *${field},\n')
+            elif field['format'] == 'guint16':
+                inner_template = ('    ${public} ${field},\n')
             elif field['format'] == 'guint32':
                 inner_template = ('    ${public} ${field},\n')
             elif field['format'] == 'guint64':
@@ -292,10 +336,14 @@
                 inner_template = ('    const gchar *const *${field},\n')
             elif field['format'] == 'struct':
                 inner_template = ('    const ${struct} *${field},\n')
+            elif field['format'] == 'ms-struct':
+                raise ValueError('type \'ms-struct\' unsupported as input')
             elif field['format'] == 'struct-array':
                 inner_template = ('    const ${struct} *const *${field},\n')
             elif field['format'] == 'ref-struct-array':
                 inner_template = ('    const ${struct} *const *${field},\n')
+            elif field['format'] == 'ms-struct-array':
+                raise ValueError('type \'ms-struct-array\' unsupported as input')
             elif field['format'] == 'ipv4':
                 inner_template = ('    const MbimIPv4 *${field},\n')
             elif field['format'] == 'ref-ipv4':
@@ -308,6 +356,14 @@
                 inner_template = ('    const MbimIPv6 *${field},\n')
             elif field['format'] == 'ipv6-array':
                 inner_template = ('    const MbimIPv6 *${field},\n')
+            elif field['format'] == 'tlv':
+                inner_template = ('    const MbimTlv *${field},\n')
+            elif field['format'] == 'tlv-string':
+                inner_template = ('    const gchar *${field},\n')
+            elif field['format'] == 'tlv-guint16-array':
+                raise ValueError('type \'tlv-guint16-array\' unsupported as input')
+            elif field['format'] == 'tlv-list':
+                inner_template = ('    const GList *${field},\n')
 
             template += (string.Template(inner_template).substitute(translations))
 
@@ -337,6 +393,8 @@
                                   '    const guint8 *${field},\n')
             elif field['format'] == 'uuid':
                 inner_template = ('    const MbimUuid *${field},\n')
+            elif field['format'] == 'guint16':
+                inner_template = ('    ${public} ${field},\n')
             elif field['format'] == 'guint32':
                 inner_template = ('    ${public} ${field},\n')
             elif field['format'] == 'guint64':
@@ -347,10 +405,14 @@
                 inner_template = ('    const gchar *const *${field},\n')
             elif field['format'] == 'struct':
                 inner_template = ('    const ${struct} *${field},\n')
+            elif field['format'] == 'ms-struct':
+                raise ValueError('type \'ms-struct\' unsupported as input')
             elif field['format'] == 'struct-array':
                 inner_template = ('    const ${struct} *const *${field},\n')
             elif field['format'] == 'ref-struct-array':
                 inner_template = ('    const ${struct} *const *${field},\n')
+            elif field['format'] == 'ms-struct-array':
+                raise ValueError('type \'ms-struct-array\' unsupported as input')
             elif field['format'] == 'ipv4':
                 inner_template = ('    const MbimIPv4 *${field},\n')
             elif field['format'] == 'ref-ipv4':
@@ -363,6 +425,14 @@
                 inner_template = ('    const MbimIPv6 *${field},\n')
             elif field['format'] == 'ipv6-array':
                 inner_template = ('    const MbimIPv6 *${field},\n')
+            elif field['format'] == 'tlv':
+                inner_template = ('    const MbimTlv *${field},\n')
+            elif field['format'] == 'tlv-string':
+                inner_template = ('    const gchar *${field},\n')
+            elif field['format'] == 'tlv-guint16-array':
+                raise ValueError('type \'tlv-guint16-array\' unsupported as input')
+            elif field['format'] == 'tlv-list':
+                inner_template = ('    const GList *${field},\n')
 
             template += (string.Template(inner_template).substitute(translations))
 
@@ -372,7 +442,7 @@
             '    MbimMessageCommandBuilder *builder;\n'
             '\n'
             '    builder = _mbim_message_command_builder_new (0,\n'
-            '                                                 MBIM_SERVICE_${service_underscore_upper},\n'
+            '                                                 ${service_enum_name},\n'
             '                                                 ${cid_enum_name},\n'
             '                                                 MBIM_MESSAGE_COMMAND_TYPE_${message_type_upper});\n')
 
@@ -407,6 +477,8 @@
                 inner_template += ('        _mbim_message_command_builder_append_byte_array (builder, FALSE, TRUE, ${pad_array}, ${field}, ${field}_size, FALSE);\n')
             elif field['format'] == 'uuid':
                 inner_template += ('        _mbim_message_command_builder_append_uuid (builder, ${field});\n')
+            elif field['format'] == 'guint16':
+                inner_template += ('        _mbim_message_command_builder_append_guint16 (builder, ${field});\n')
             elif field['format'] == 'guint32':
                 inner_template += ('        _mbim_message_command_builder_append_guint32 (builder, ${field});\n')
             elif field['format'] == 'guint64':
@@ -417,10 +489,14 @@
                 inner_template += ('        _mbim_message_command_builder_append_string_array (builder, ${field}, ${array_size_field});\n')
             elif field['format'] == 'struct':
                 inner_template += ('        _mbim_message_command_builder_append_${struct_underscore}_struct (builder, ${field});\n')
+            elif field['format'] == 'ms-struct':
+                raise ValueError('type \'ms-struct\' unsupported as input')
             elif field['format'] == 'struct-array':
-                inner_template += ('        _mbim_message_command_builder_append_${struct_underscore}_struct_array (builder, ${field}, ${array_size_field}, FALSE);\n')
+                inner_template += ('        _mbim_message_command_builder_append_${struct_underscore}_struct_array (builder, ${field}, ${array_size_field});\n')
             elif field['format'] == 'ref-struct-array':
-                inner_template += ('        _mbim_message_command_builder_append_${struct_underscore}_struct_array (builder, ${field}, ${array_size_field}, TRUE);\n')
+                inner_template += ('        _mbim_message_command_builder_append_${struct_underscore}_ref_struct_array (builder, ${field}, ${array_size_field});\n')
+            elif field['format'] == 'ms-struct-array':
+                raise ValueError('type \'ms-struct-array\' unsupported as input')
             elif field['format'] == 'ipv4':
                 inner_template += ('        _mbim_message_command_builder_append_ipv4 (builder, ${field}, FALSE);\n')
             elif field['format'] == 'ref-ipv4':
@@ -433,6 +509,15 @@
                 inner_template += ('        _mbim_message_command_builder_append_ipv6 (builder, ${field}, TRUE);\n')
             elif field['format'] == 'ipv6-array':
                 inner_template += ('        _mbim_message_command_builder_append_ipv6_array (builder, ${field}, ${array_size_field});\n')
+            elif field['format'] == 'tlv':
+                inner_template += ('        _mbim_message_command_builder_append_tlv (builder, ${field});\n')
+            elif field['format'] == 'tlv-string':
+                inner_template += ('        _mbim_message_command_builder_append_tlv_string (builder, ${field});\n')
+            elif field['format'] == 'tlv-guint16-array':
+                raise ValueError('type \'tlv-guint16-array\' unsupported as input')
+            elif field['format'] == 'tlv-list':
+                inner_template += ('        _mbim_message_command_builder_append_tlv_list (builder, ${field});\n')
+
             else:
                 raise ValueError('Cannot handle field type \'%s\'' % field['format'])
 
@@ -451,13 +536,12 @@
     Emit message parser
     """
     def _emit_message_parser(self, hfile, cfile, message_type, fields, since):
-        translations = { 'message'                  : self.name,
-                         'service'                  : self.service,
-                         'since'                    : since,
-                         'underscore'               : utils.build_underscore_name (self.fullname),
-                         'message_type'             : message_type,
-                         'message_type_upper'       : message_type.upper(),
-                         'service_underscore_upper' : utils.build_underscore_name (self.service).upper() }
+        translations = { 'message'            : self.name,
+                         'service'            : self.service,
+                         'since'              : since,
+                         'underscore'         : utils.build_underscore_name (self.fullname),
+                         'message_type'       : message_type,
+                         'message_type_upper' : message_type.upper() }
 
         template = (
             '\n'
@@ -481,6 +565,8 @@
                                   ' * @out_${field}: (out)(optional)(transfer none)(element-type guint8)(array length=out_${field}_size): return location for an array of #guint8 values. Do not free the returned value, it is owned by @message.\n')
             elif field['format'] == 'uuid':
                 inner_template = (' * @out_${field}: (out)(optional)(transfer none): return location for a #MbimUuid, or %NULL if the \'${name}\' field is not needed. Do not free the returned value, it is owned by @message.\n')
+            elif field['format'] == 'guint16':
+                inner_template = (' * @out_${field}: (out)(optional)(transfer none): return location for a #${public}, or %NULL if the \'${name}\' field is not needed.\n')
             elif field['format'] == 'guint32':
                 inner_template = (' * @out_${field}: (out)(optional)(transfer none): return location for a #${public}, or %NULL if the \'${name}\' field is not needed.\n')
             elif field['format'] == 'guint64':
@@ -491,10 +577,15 @@
                 inner_template = (' * @out_${field}: (out)(optional)(transfer full)(type GStrv): return location for a newly allocated array of strings, or %NULL if the \'${name}\' field is not needed. Free the returned value with g_strfreev().\n')
             elif field['format'] == 'struct':
                 inner_template = (' * @out_${field}: (out)(optional)(transfer full): return location for a newly allocated #${struct}, or %NULL if the \'${name}\' field is not needed. Free the returned value with ${struct_underscore}_free().\n')
+            elif field['format'] == 'ms-struct':
+                inner_template = (' * @out_${field}: (out)(optional)(nullable)(transfer full): return location for a newly allocated #${struct}, or %NULL if the \'${name}\' field is not needed. The availability of this field is not always guaranteed, and therefore %NULL may be given as a valid output. Free the returned value with ${struct_underscore}_free().\n')
             elif field['format'] == 'struct-array':
                 inner_template = (' * @out_${field}: (out)(optional)(transfer full)(array zero-terminated=1)(element-type ${struct}): return location for a newly allocated array of #${struct} items, or %NULL if the \'${name}\' field is not needed. Free the returned value with ${struct_underscore}_array_free().\n')
             elif field['format'] == 'ref-struct-array':
                 inner_template = (' * @out_${field}: (out)(optional)(transfer full)(array zero-terminated=1)(element-type ${struct}): return location for a newly allocated array of #${struct} items, or %NULL if the \'${name}\' field is not needed. Free the returned value with ${struct_underscore}_array_free().\n')
+            elif field['format'] == 'ms-struct-array':
+                inner_template = (' * @out_${field}_count: (out)(optional)(transfer none): return location for a #guint32, or %NULL if the field is not needed.\n'
+                                  ' * @out_${field}: (out)(optional)(nullable)(transfer full)(array zero-terminated=1)(element-type ${struct}): return location for a newly allocated array of #${struct} items, or %NULL if the \'${name}\' field is not needed. The availability of this field is not always guaranteed, and therefore %NULL may be given as a valid output. Free the returned value with ${struct_underscore}_array_free().\n')
             elif field['format'] == 'ipv4':
                 inner_template = (' * @out_${field}: (out)(optional)(transfer none): return location for a #MbimIPv4, or %NULL if the \'${name}\' field is not needed. Do not free the returned value, it is owned by @message.\n')
             elif field['format'] == 'ref-ipv4':
@@ -507,6 +598,15 @@
                 inner_template = (' * @out_${field}: (out)(optional)(transfer none): return location for a #MbimIPv6, or %NULL if the \'${name}\' field is not needed. Do not free the returned value, it is owned by @message.\n')
             elif field['format'] == 'ipv6-array':
                 inner_template = (' * @out_${field}: (out)(optional)(transfer full)(array zero-terminated=1)(element-type MbimIPv6): return location for a newly allocated array of #MbimIPv6 items, or %NULL if the \'${name}\' field is not needed. Free the returned value with g_free().\n')
+            elif field['format'] == 'tlv':
+                inner_template = (' * @out_${field}: (out)(optional)(transfer full): return location for a newly allocated #MbimTlv, or %NULL if the \'${name}\' field is not needed. Free the returned value with mbim_tlv_unref().\n')
+            elif field['format'] == 'tlv-string':
+                inner_template = (' * @out_${field}: (out)(optional)(transfer full): return location for a newly allocated string, or %NULL if the \'${name}\' field is not needed. Free the returned value with g_free().\n')
+            elif field['format'] == 'tlv-guint16-array':
+                inner_template = (' * @out_${field}_count: (out)(optional)(transfer none): return location for a #guint32, or %NULL if the field is not needed.\n'
+                                  ' * @out_${field}: (out)(optional)(nullable)(transfer full): return location for a newly allocated array of #guint16 items, or %NULL if the \'${name}\' field is not needed. The availability of this field is not always guaranteed, and therefore %NULL may be given as a valid output. Free the returned value with g_free().\n')
+            elif field['format'] == 'tlv-list':
+                inner_template = (' * @out_${field}: (out)(optional)(element-type MbimTlv)(transfer full): return location for a newly allocated list of #MbimTlv items, or %NULL if the \'${name}\' field is not needed. Free the returned value with g_list_free_full() using mbim_tlv_unref() as #GDestroyNotify.\n')
 
             template += (string.Template(inner_template).substitute(translations))
 
@@ -535,6 +635,8 @@
                                   '    const guint8 **out_${field},\n')
             elif field['format'] == 'uuid':
                 inner_template = ('    const MbimUuid **out_${field},\n')
+            elif field['format'] == 'guint16':
+                inner_template = ('    ${public} *out_${field},\n')
             elif field['format'] == 'guint32':
                 inner_template = ('    ${public} *out_${field},\n')
             elif field['format'] == 'guint64':
@@ -545,10 +647,15 @@
                 inner_template = ('    gchar ***out_${field},\n')
             elif field['format'] == 'struct':
                 inner_template = ('    ${struct} **out_${field},\n')
+            elif field['format'] == 'ms-struct':
+                inner_template = ('    ${struct} **out_${field},\n')
             elif field['format'] == 'struct-array':
                 inner_template = ('    ${struct}Array **out_${field},\n')
             elif field['format'] == 'ref-struct-array':
                 inner_template = ('    ${struct}Array **out_${field},\n')
+            elif field['format'] == 'ms-struct-array':
+                inner_template = ('    guint32 *out_${field}_count,\n'
+                                  '    ${struct}Array **out_${field},\n')
             elif field['format'] == 'ipv4':
                 inner_template = ('    const MbimIPv4 **out_${field},\n')
             elif field['format'] == 'ref-ipv4':
@@ -561,6 +668,15 @@
                 inner_template = ('    const MbimIPv6 **out_${field},\n')
             elif field['format'] == 'ipv6-array':
                 inner_template = ('    MbimIPv6 **out_${field},\n')
+            elif field['format'] == 'tlv':
+                inner_template = ('    MbimTlv **out_${field},\n')
+            elif field['format'] == 'tlv-string':
+                inner_template = ('    gchar **out_${field},\n')
+            elif field['format'] == 'tlv-guint16-array':
+                inner_template = ('    guint32 *out_${field}_count,\n'
+                                  '    guint16 **out_${field},\n')
+            elif field['format'] == 'tlv-list':
+                inner_template = ('    GList **out_${field},\n')
             else:
                 raise ValueError('Cannot handle field type \'%s\'' % field['format'])
 
@@ -589,6 +705,8 @@
                                   '    const guint8 **out_${field},\n')
             elif field['format'] == 'uuid':
                 inner_template = ('    const MbimUuid **out_${field},\n')
+            elif field['format'] == 'guint16':
+                inner_template = ('    ${public} *out_${field},\n')
             elif field['format'] == 'guint32':
                 inner_template = ('    ${public} *out_${field},\n')
             elif field['format'] == 'guint64':
@@ -599,10 +717,15 @@
                 inner_template = ('    gchar ***out_${field},\n')
             elif field['format'] == 'struct':
                 inner_template = ('    ${struct} **out_${field},\n')
+            elif field['format'] == 'ms-struct':
+                inner_template = ('    ${struct} **out_${field},\n')
             elif field['format'] == 'struct-array':
                 inner_template = ('    ${struct}Array **out_${field},\n')
             elif field['format'] == 'ref-struct-array':
                 inner_template = ('    ${struct}Array **out_${field},\n')
+            elif field['format'] == 'ms-struct-array':
+                inner_template = ('    guint32 *out_${field}_count,\n'
+                                  '    ${struct}Array **out_${field},\n')
             elif field['format'] == 'ipv4':
                 inner_template = ('    const MbimIPv4 **out_${field},\n')
             elif field['format'] == 'ref-ipv4':
@@ -615,6 +738,15 @@
                 inner_template = ('    const MbimIPv6 **out_${field},\n')
             elif field['format'] == 'ipv6-array':
                 inner_template = ('    MbimIPv6 **out_${field},\n')
+            elif field['format'] == 'tlv':
+                inner_template = ('    MbimTlv **out_${field},\n')
+            elif field['format'] == 'tlv-string':
+                inner_template = ('    gchar **out_${field},\n')
+            elif field['format'] == 'tlv-guint16-array':
+                inner_template = ('    guint32 *out_${field}_count,\n'
+                                  '    guint16 **out_${field},\n')
+            elif field['format'] == 'tlv-list':
+                inner_template = ('    GList **out_${field},\n')
 
             template += (string.Template(inner_template).substitute(translations))
 
@@ -645,18 +777,36 @@
             elif field['format'] == 'struct':
                 count_allocated_variables += 1
                 inner_template = ('    ${struct} *_${field} = NULL;\n')
+            elif field['format'] == 'ms-struct':
+                count_allocated_variables += 1
+                inner_template = ('    ${struct} *_${field} = NULL;\n')
             elif field['format'] == 'struct-array':
                 count_allocated_variables += 1
                 inner_template = ('    ${struct} **_${field} = NULL;\n')
             elif field['format'] == 'ref-struct-array':
                 count_allocated_variables += 1
                 inner_template = ('    ${struct} **_${field} = NULL;\n')
+            elif field['format'] == 'ms-struct-array':
+                count_allocated_variables += 1
+                inner_template = ('    ${struct} **_${field} = NULL;\n')
             elif field['format'] == 'ipv4-array':
                 count_allocated_variables += 1
                 inner_template = ('    MbimIPv4 *_${field} = NULL;\n')
             elif field['format'] == 'ipv6-array':
                 count_allocated_variables += 1
                 inner_template = ('    MbimIPv6 *_${field} = NULL;\n')
+            elif field['format'] == 'tlv':
+                count_allocated_variables += 1
+                inner_template = ('    MbimTlv *_${field} = NULL;\n')
+            elif field['format'] == 'tlv-string':
+                count_allocated_variables += 1
+                inner_template = ('    gchar *_${field} = NULL;\n')
+            elif field['format'] == 'tlv-guint16-array':
+                count_allocated_variables += 1
+                inner_template = ('    guint16 *_${field} = NULL;\n')
+            elif field['format'] == 'tlv-list':
+                count_allocated_variables += 1
+                inner_template = ('    GList *_${field} = NULL;\n')
             template += (string.Template(inner_template).substitute(translations))
 
         if message_type == 'response':
@@ -738,6 +888,7 @@
                 elif field['format'] == 'string' or \
                      field['format'] == 'string-array' or \
                      field['format'] == 'struct' or \
+                     field['format'] == 'ms-struct' or \
                      field['format'] == 'struct-array' or \
                      field['format'] == 'ref-struct-array' or \
                      field['format'] == 'ipv4' or \
@@ -749,6 +900,12 @@
                     inner_template += (
                         '        if (out_${field} != NULL)\n'
                         '            *out_${field} = NULL;\n')
+                elif field['format'] == 'ms-struct-array':
+                    inner_template += (
+                        '        if (out_${field}_count != NULL)\n'
+                        '            *out_${field}_count = 0;\n'
+                        '        if (out_${field} != NULL)\n'
+                        '            *out_${field} = NULL;\n')
                 else:
                     raise ValueError('Field format \'%s\' unsupported as optional field' % field['format'])
 
@@ -815,6 +972,23 @@
                     '        if ((out_${field} != NULL) && !_mbim_message_read_uuid (message, offset, out_${field}, error))\n'
                     '            goto out;\n'
                     '        offset += 16;\n')
+            elif field['format'] == 'guint16':
+                if 'public-format' in field:
+                    translations['public'] = field['public-format'] if 'public-format' in field else field['format']
+                    inner_template += (
+                        '        if (out_${field} != NULL) {\n'
+                        '            guint16 aux;\n'
+                        '\n'
+                        '            if (!_mbim_message_read_guint16 (message, offset, &aux, error))\n'
+                        '                goto out;\n'
+                        '            *out_${field} = (${public})aux;\n'
+                        '        }\n')
+                else:
+                    inner_template += (
+                        '        if ((out_${field} != NULL) && !_mbim_message_read_guint16 (message, offset, out_${field}, error))\n'
+                        '            goto out;\n')
+                inner_template += (
+                    '        offset += 2;\n')
             elif field['format'] == 'guint32':
                 if 'public-format' in field:
                     translations['public'] = field['public-format'] if 'public-format' in field else field['format']
@@ -872,16 +1046,32 @@
                     '        else\n'
                     '             _${struct_name}_free (tmp);\n'
                     '        offset += bytes_read;\n')
+            elif field['format'] == 'ms-struct':
+                inner_template += (
+                    '        ${struct_type} *tmp = NULL;\n'
+                    '\n'
+                    '        if (!_mbim_message_read_${struct_name}_ms_struct (message, offset, &tmp, error))\n'
+                    '            goto out;\n'
+                    '        if (out_${field} != NULL)\n'
+                    '            _${field} = tmp;\n'
+                    '        else\n'
+                    '             _${struct_name}_free (tmp);\n'
+                    '        offset += 8;\n')
             elif field['format'] == 'struct-array':
                 inner_template += (
-                    '        if ((out_${field} != NULL) && !_mbim_message_read_${struct_name}_struct_array (message, _${array_size_field}, offset, FALSE, &_${field}, error))\n'
+                    '        if ((out_${field} != NULL) && !_mbim_message_read_${struct_name}_struct_array (message, _${array_size_field}, offset, &_${field}, error))\n'
                     '            goto out;\n'
                     '        offset += 4;\n')
             elif field['format'] == 'ref-struct-array':
                 inner_template += (
-                    '        if ((out_${field} != NULL) && !_mbim_message_read_${struct_name}_struct_array (message, _${array_size_field}, offset, TRUE, &_${field}, error))\n'
+                    '        if ((out_${field} != NULL) && !_mbim_message_read_${struct_name}_ref_struct_array (message, _${array_size_field}, offset, &_${field}, error))\n'
                     '            goto out;\n'
                     '        offset += (8 * _${array_size_field});\n')
+            elif field['format'] == 'ms-struct-array':
+                inner_template += (
+                    '        if ((out_${field} != NULL) && !_mbim_message_read_${struct_name}_ms_struct_array (message, offset, out_${field}_count, &_${field}, error))\n'
+                    '            goto out;\n'
+                    '        offset += 8;\n')
             elif field['format'] == 'ipv4':
                 inner_template += (
                     '        if ((out_${field} != NULL) && !_mbim_message_read_ipv4 (message, offset, FALSE, out_${field}, error))\n'
@@ -912,6 +1102,54 @@
                     '        if ((out_${field} != NULL) && !_mbim_message_read_ipv6_array (message, _${array_size_field}, offset, &_${field}, error))\n'
                     '            goto out;\n'
                     '        offset += 4;\n')
+            elif field['format'] == 'tlv':
+                inner_template += (
+                    '        MbimTlv *tmp = NULL;\n'
+                    '        guint32 bytes_read = 0;\n'
+                    '\n'
+                    '        if (!_mbim_message_read_tlv (message, offset, &tmp, &bytes_read, error))\n'
+                    '            goto out;\n'
+                    '        if (out_${field} != NULL)\n'
+                    '            _${field} = tmp;\n'
+                    '        else\n'
+                    '             mbim_tlv_unref (tmp);\n'
+                    '        offset += bytes_read;\n')
+            elif field['format'] == 'tlv-string':
+                inner_template += (
+                    '        gchar *tmp = NULL;\n'
+                    '        guint32 bytes_read = 0;\n'
+                    '\n'
+                    '        if (!_mbim_message_read_tlv_string (message, offset, &tmp, &bytes_read, error))\n'
+                    '            goto out;\n'
+                    '        if (out_${field} != NULL)\n'
+                    '            _${field} = tmp;\n'
+                    '        else\n'
+                    '             g_free (tmp);\n'
+                    '        offset += bytes_read;\n')
+            elif field['format'] == 'tlv-guint16-array':
+                inner_template += (
+                    '        guint16 *tmp = NULL;\n'
+                    '        guint32 bytes_read = 0;\n'
+                    '\n'
+                    '        if (!_mbim_message_read_tlv_guint16_array (message, offset, out_${field}_count, &tmp, &bytes_read, error))\n'
+                    '            goto out;\n'
+                    '        if (out_${field} != NULL)\n'
+                    '            _${field} = tmp;\n'
+                    '        else\n'
+                    '             g_free (tmp);\n'
+                    '        offset += bytes_read;\n')
+            elif field['format'] == 'tlv-list':
+                inner_template += (
+                    '        GList *tmp = NULL;\n'
+                    '        guint32 bytes_read = 0;\n'
+                    '\n'
+                    '        if (!_mbim_message_read_tlv_list (message, offset, &tmp, &bytes_read, error))\n'
+                    '            goto out;\n'
+                    '        if (out_${field} != NULL)\n'
+                    '            _${field} = tmp;\n'
+                    '        else\n'
+                    '             g_list_free_full (tmp, (GDestroyNotify)mbim_tlv_unref);\n'
+                    '        offset += bytes_read;\n')
 
             inner_template += (
                 '    }\n')
@@ -938,10 +1176,16 @@
                 if field['format'] == 'string' or \
                    field['format'] == 'string-array' or \
                    field['format'] == 'struct' or \
+                   field['format'] == 'ms-struct' or \
                    field['format'] == 'struct-array' or \
                    field['format'] == 'ref-struct-array' or \
+                   field['format'] == 'ms-struct-array' or \
                    field['format'] == 'ipv4-array' or \
-                   field['format'] == 'ipv6-array':
+                   field['format'] == 'ipv6-array' or \
+                   field['format'] == 'tlv' or \
+                   field['format'] == 'tlv-string' or \
+                   field['format'] == 'tlv-guint16-array' or \
+                   field['format'] == 'tlv-list':
                     inner_template = ('        if (out_${field} != NULL)\n'
                                       '            *out_${field} = _${field};\n')
                     template += (string.Template(inner_template).substitute(translations))
@@ -954,14 +1198,21 @@
                 inner_template = ''
                 if field['format'] == 'string' or \
                    field['format'] == 'ipv4-array' or \
-                   field['format'] == 'ipv6-array':
+                   field['format'] == 'ipv6-array' or \
+                   field['format'] == 'tlv-string' or \
+                   field['format'] == 'tlv-guint16-array':
                     inner_template = ('        g_free (_${field});\n')
                 elif field['format'] == 'string-array':
                     inner_template = ('        g_strfreev (_${field});\n')
-                elif field['format'] == 'struct':
+                elif field['format'] == 'struct' or field['format'] == 'ms-struct':
                     inner_template = ('        ${struct_underscore}_free (_${field});\n')
-                elif field['format'] == 'struct-array' or field['format'] == 'ref-struct-array':
+                elif field['format'] == 'struct-array' or field['format'] == 'ref-struct-array' or field['format'] == 'ms-struct-array':
                     inner_template = ('        ${struct_underscore}_array_free (_${field});\n')
+                elif field['format'] == 'tlv':
+                    inner_template = ('        if (_${field})\n'
+                                      '            mbim_tlv_unref (_${field});\n')
+                elif field['format'] == 'tlv-list':
+                    inner_template = ('        g_list_free_full (_${field}, (GDestroyNotify)mbim_tlv_unref);\n')
                 template += (string.Template(inner_template).substitute(translations))
             template += (
                 '    }\n')
@@ -983,13 +1234,12 @@
     Emit message printable
     """
     def _emit_message_printable(self, cfile, message_type, fields):
-        translations = { 'message'                  : self.name,
-                         'underscore'               : utils.build_underscore_name(self.name),
-                         'service'                  : self.service,
-                         'underscore'               : utils.build_underscore_name (self.fullname),
-                         'message_type'             : message_type,
-                         'message_type_upper'       : message_type.upper(),
-                         'service_underscore_upper' : utils.build_underscore_name (self.service).upper() }
+        translations = { 'message'            : self.name,
+                         'underscore'         : utils.build_underscore_name(self.name),
+                         'service'            : self.service,
+                         'underscore'         : utils.build_underscore_name (self.fullname),
+                         'message_type'       : message_type,
+                         'message_type_upper' : message_type.upper() }
         template = (
             '\n'
             'static gchar *\n'
@@ -1026,8 +1276,6 @@
             translations['field_format']            = field['format']
             translations['field_format_underscore'] = utils.build_underscore_name_from_camelcase(field['format'])
             translations['public']                  = field['public-format'] if 'public-format' in field else field['format']
-            translations['public_underscore']       = utils.build_underscore_name_from_camelcase(field['public-format']) if 'public-format' in field else ''
-            translations['public_underscore_upper'] = utils.build_underscore_name_from_camelcase(field['public-format']).upper() if 'public-format' in field else ''
             translations['field_name']              = field['name']
             translations['array_size_field']        = utils.build_underscore_name_from_camelcase(field['array-size-field']) if 'array-size-field' in field else ''
             translations['struct_name']             = utils.build_underscore_name_from_camelcase(field['struct-type']) if 'struct-type' in field else ''
@@ -1113,12 +1361,18 @@
                     '        tmpstr = mbim_uuid_get_printable (tmp);\n'
                     '        g_string_append_printf (str, "\'%s\'", tmpstr);\n')
 
-            elif field['format'] == 'guint32' or \
+            elif field['format'] == 'guint16' or \
+                 field['format'] == 'guint32' or \
                  field['format'] == 'guint64':
                 inner_template += (
                     '        ${field_format} tmp;\n'
                     '\n')
-                if field['format'] == 'guint32' :
+                if field['format'] == 'guint16' :
+                    inner_template += (
+                        '        if (!_mbim_message_read_guint16 (message, offset, &tmp, &inner_error))\n'
+                        '            goto out;\n'
+                        '        offset += 2;\n')
+                elif field['format'] == 'guint32' :
                     inner_template += (
                         '        if (!_mbim_message_read_guint32 (message, offset, &tmp, &inner_error))\n'
                         '            goto out;\n'
@@ -1130,20 +1384,31 @@
                         '        offset += 8;\n')
 
                 if 'public-format' in field:
+                    if field['public-format'] == 'gboolean':
+                        inner_template += (
+                            '        g_string_append_printf (str, "\'%s\'", tmp ? "true" : "false");\n'
+                            '\n')
+                    else:
+                        translations['public_underscore']       = utils.build_underscore_name_from_camelcase(field['public-format'])
+                        translations['public_underscore_upper'] = utils.build_underscore_name_from_camelcase(field['public-format']).upper()
+                        inner_template += (
+                            '#if defined __${public_underscore_upper}_IS_ENUM__\n'
+                            '        g_string_append_printf (str, "\'%s\'", ${public_underscore}_get_string ((${public})tmp));\n'
+                            '#elif defined __${public_underscore_upper}_IS_FLAGS__\n'
+                            '        {\n'
+                            '            g_autofree gchar *tmpstr = NULL;\n'
+                            '\n'
+                            '            tmpstr = ${public_underscore}_build_string_from_mask ((${public})tmp);\n'
+                            '            g_string_append_printf (str, "\'%s\'", tmpstr);\n'
+                            '        }\n'
+                            '#else\n'
+                            '# error neither enum nor flags\n'
+                            '#endif\n'
+                            '\n')
+
+                elif field['format'] == 'guint16':
                     inner_template += (
-                        '#if defined __${public_underscore_upper}_IS_ENUM__\n'
-                        '        g_string_append_printf (str, "\'%s\'", ${public_underscore}_get_string ((${public})tmp));\n'
-                        '#elif defined __${public_underscore_upper}_IS_FLAGS__\n'
-                        '        {\n'
-                        '            g_autofree gchar *tmpstr = NULL;\n'
-                        '\n'
-                        '            tmpstr = ${public_underscore}_build_string_from_mask ((${public})tmp);\n'
-                        '            g_string_append_printf (str, "\'%s\'", tmpstr);\n'
-                        '        }\n'
-                        '#else\n'
-                        '# error neither enum nor flags\n'
-                        '#endif\n'
-                        '\n')
+                        '        g_string_append_printf (str, "\'%" G_GUINT16_FORMAT "\'", tmp);\n')
                 elif field['format'] == 'guint32':
                     inner_template += (
                         '        g_string_append_printf (str, "\'%" G_GUINT32_FORMAT "\'", tmp);\n')
@@ -1192,30 +1457,64 @@
                     '        new_line_prefix = g_strdup_printf ("%s    ", line_prefix);\n'
                     '        struct_str = _mbim_message_print_${struct_name}_struct (tmp, new_line_prefix);\n'
                     '        g_string_append (str, struct_str);\n'
-                    '        g_string_append_printf (str, "%s  }\\n", line_prefix);\n')
+                    '        g_string_append_printf (str, "%s  }", line_prefix);\n')
 
-            elif field['format'] == 'struct-array' or field['format'] == 'ref-struct-array':
+            elif field['format'] == 'ms-struct':
+                inner_template += (
+                    '        g_autoptr(${struct_type}) tmp = NULL;\n'
+                    '        g_autofree gchar *new_line_prefix = NULL;\n'
+                    '\n'
+                    '        if (!_mbim_message_read_${struct_name}_ms_struct (message, offset, &tmp, &inner_error))\n'
+                    '            goto out;\n'
+                    '        offset += 8;\n'
+                    '        g_string_append (str, "{\\n");\n'
+                    '        new_line_prefix = g_strdup_printf ("%s    ", line_prefix);\n'
+                    '        if (tmp) {\n'
+                    '            g_autofree gchar *struct_str = NULL;\n'
+                    '            struct_str = _mbim_message_print_${struct_name}_struct (tmp, new_line_prefix);\n'
+                    '            g_string_append (str, struct_str);\n'
+                    '        }\n'
+                    '        g_string_append_printf (str, "%s  }", line_prefix);\n')
+
+            elif field['format'] == 'struct-array' or field['format'] == 'ref-struct-array' or field['format'] == 'ms-struct-array':
                 inner_template += (
                     '        g_autoptr(${struct_type}Array) tmp = NULL;\n'
                     '        g_autofree gchar *new_line_prefix = NULL;\n'
-                    '        guint i;\n'
+                    '        guint i;\n')
+                if field['format'] == 'ms-struct-array':
+                    inner_template += (
+                        '        guint32 tmp_count = 0;\n')
+                inner_template += (
                     '\n')
 
                 if field['format'] == 'struct-array':
                     inner_template += (
-                    '        if (!_mbim_message_read_${struct_name}_struct_array (message, _${array_size_field}, offset, FALSE, &tmp, &inner_error))\n'
+                    '        if (!_mbim_message_read_${struct_name}_struct_array (message, _${array_size_field}, offset, &tmp, &inner_error))\n'
                     '            goto out;\n'
                     '        offset += 4;\n')
                 elif field['format'] == 'ref-struct-array':
                     inner_template += (
-                    '        if (!_mbim_message_read_${struct_name}_struct_array (message, _${array_size_field}, offset, TRUE, &tmp, &inner_error))\n'
+                    '        if (!_mbim_message_read_${struct_name}_ref_struct_array (message, _${array_size_field}, offset, &tmp, &inner_error))\n'
                     '            goto out;\n'
                     '        offset += (8 * _${array_size_field});\n')
+                elif field['format'] == 'ms-struct-array':
+                    inner_template += (
+                    '        if (!_mbim_message_read_${struct_name}_ms_struct_array (message, offset, &tmp_count, &tmp, &inner_error))\n'
+                    '            goto out;\n'
+                    '        offset += 8;\n')
 
                 inner_template += (
                     '        new_line_prefix = g_strdup_printf ("%s        ", line_prefix);\n'
-                    '        g_string_append (str, "\'{\\n");\n'
-                    '        for (i = 0; i < _${array_size_field}; i++) {\n'
+                    '        g_string_append (str, "\'{\\n");\n')
+
+                if field['format'] == 'ms-struct-array':
+                    inner_template += (
+                        '        for (i = 0; i < tmp_count; i++) {\n')
+                else:
+                    inner_template += (
+                        '        for (i = 0; i < _${array_size_field}; i++) {\n')
+
+                inner_template += (
                     '            g_autofree gchar *struct_str = NULL;\n'
                     '\n'
                     '            g_string_append_printf (str, "%s    [%u] = {\\n", line_prefix, i);\n'
@@ -1316,6 +1615,45 @@
                     '        }\n'
                     '        g_string_append (str, "\'");\n')
 
+            elif field['format'] == 'tlv' or \
+                 field['format'] == 'tlv-string' or \
+                 field['format'] == 'tlv-guint16-array':
+                inner_template += (
+                    '        g_autoptr(MbimTlv) tmp = NULL;\n'
+                    '        guint32 bytes_read = 0;\n'
+                    '        g_autofree gchar *tlv_str = NULL;\n'
+                    '        g_autofree gchar *new_line_prefix = NULL;\n'
+                    '\n'
+                    '        if (!_mbim_message_read_tlv (message, offset, &tmp, &bytes_read, &inner_error))\n'
+                    '            goto out;\n'
+                    '        offset += bytes_read;\n'
+                    '\n'
+                    '        new_line_prefix = g_strdup_printf ("%s  ", line_prefix);\n'
+                    '        tlv_str = _mbim_tlv_print (tmp, new_line_prefix);\n'
+                    '        g_string_append_printf (str, "\'%s\'", tlv_str);\n')
+
+            elif field['format'] == 'tlv-list':
+                inner_template += (
+                    '        GList *tmp = NULL;\n'
+                    '        GList *walker = NULL;\n'
+                    '        guint32 bytes_read = 0;\n'
+                    '        g_autofree gchar *new_line_prefix = NULL;\n'
+                    '\n'
+                    '        if (!_mbim_message_read_tlv_list (message, offset, &tmp, &bytes_read, &inner_error))\n'
+                    '            goto out;\n'
+                    '        offset += bytes_read;\n'
+                    '\n'
+                    '        new_line_prefix = g_strdup_printf ("%s    ", line_prefix);\n'
+                    '        g_string_append (str, "\'[ ");\n'
+                    '        for (walker = tmp; walker; walker = g_list_next (walker)) {\n'
+                    '            g_autofree gchar *tlv_str = NULL;\n'
+                    '\n'
+                    '            tlv_str = _mbim_tlv_print ((MbimTlv *)walker->data, new_line_prefix);\n'
+                    '            g_string_append_printf (str, "%s,", tlv_str);\n'
+                    '        }\n'
+                    '        g_string_append_printf (str, "\\n%s  ]\'", line_prefix);\n'
+                    '        g_list_free_full (tmp, (GDestroyNotify)mbim_tlv_unref);\n')
+
             else:
                 raise ValueError('Field format \'%s\' not printable' % field['format'])
 
diff --git a/build-aux/mbim-codegen/ObjectList.py b/build-aux/mbim-codegen/ObjectList.py
index 3faf1ee..bb3d45a 100644
--- a/build-aux/mbim-codegen/ObjectList.py
+++ b/build-aux/mbim-codegen/ObjectList.py
@@ -19,11 +19,18 @@
         if field['format'] == 'struct' and field['struct-type'] == struct.name:
             struct.single_member = True
             break
+        if field['format'] == 'ms-struct' and field['struct-type'] == struct.name:
+            struct.single_member = True
+            struct.ms_struct = True
+            break
         if field['format'] == 'ref-struct-array' and field['struct-type'] == struct.name:
-            struct.array_member = True
+            struct.ref_struct_array_member = True
             break
         if field['format'] == 'struct-array' and field['struct-type'] == struct.name:
-            struct.array_member = True
+            struct.struct_array_member = True
+            break
+        if field['format'] == 'ms-struct-array' and field['struct-type'] == struct.name:
+            struct.ms_struct_array_member = True
             break
 
 
@@ -39,20 +46,33 @@
     def __init__(self, objects_dictionary):
         self.command_list = []
         self.struct_list = []
-        self.service = ''
+        self.service_list = []
 
         # Loop items in the list, creating Message objects for the messages
+        service_iter = ''
+        mbimex_service_iter = ''
+        mbimex_version_iter = ''
+
         for object_dictionary in objects_dictionary:
             if object_dictionary['type'] == 'Command':
-                self.command_list.append(Message(object_dictionary))
+                if service_iter == '':
+                    raise ValueError('Service name not specified before the first command')
+                self.command_list.append(Message(service_iter, mbimex_service_iter, mbimex_version_iter, object_dictionary))
             elif object_dictionary['type'] == 'Struct':
                 self.struct_list.append(Struct(object_dictionary))
             elif object_dictionary['type'] == 'Service':
-                self.service = object_dictionary['name']
+                service_iter = object_dictionary['name']
+                self.service_list.append(service_iter)
+                if 'mbimex-service' in object_dictionary:
+                    mbimex_service_iter = object_dictionary['mbimex-service']
+                    mbimex_version_iter = object_dictionary['mbimex-version']
+                else:
+                    mbimex_service_iter = ''
+                    mbimex_version_iter = ''
             else:
                 raise ValueError('Cannot handle object type \'%s\'' % object_dictionary['type'])
 
-        if self.service == '':
+        if not self.service_list:
             raise ValueError('Service name not specified')
 
         # Populate struct usages
@@ -76,61 +96,47 @@
 
 
     """
-    Emit support for printing messages in this service
+    Emit support for printing messages in a single service
     """
-    def emit_printable(self, hfile, cfile):
-        translations = { 'service_underscore' : utils.build_underscore_name(self.service),
-                         'service'            : self.service }
-
+    def emit_printable_service(self, hfile, cfile, service):
+        translations = { 'service_underscore' : utils.build_underscore_name(service),
+                         'service'            : service }
 
         template = (
             '\n'
-            '/*****************************************************************************/\n'
-            '/* Service helper for printable fields */\n'
-            '\n'
-            '#if defined (LIBMBIM_GLIB_COMPILATION)\n'
-            '\n'
             'G_GNUC_INTERNAL\n'
             'gchar *\n'
             '__mbim_message_${service_underscore}_get_printable_fields (\n'
             '    const MbimMessage *message,\n'
             '    const gchar *line_prefix,\n'
-            '    GError **error);\n'
-            '\n'
-            '#endif\n')
+            '    GError **error);\n')
         hfile.write(string.Template(template).substitute(translations))
 
         template = (
             '\n'
-            'typedef struct {\n'
-            '  gchar * (* query_cb)        (const MbimMessage *message, const gchar *line_prefix, GError **error);\n'
-            '  gchar * (* set_cb)          (const MbimMessage *message, const gchar *line_prefix, GError **error);\n'
-            '  gchar * (* response_cb)     (const MbimMessage *message, const gchar *line_prefix, GError **error);\n'
-            '  gchar * (* notification_cb) (const MbimMessage *message, const gchar *line_prefix, GError **error);\n'
-            '} GetPrintableCallbacks;\n'
-            '\n'
-            'static const GetPrintableCallbacks get_printable_callbacks[] = {\n')
+            'static const GetPrintableCallbacks ${service_underscore}_get_printable_callbacks[] = {\n')
 
         for item in self.command_list:
-            translations['message'] = utils.build_underscore_name (item.fullname)
-            translations['cid']     = item.cid_enum_name
-            inner_template = (
-                '    [${cid}] = {\n')
-            if item.has_query:
+            if item.service == service:
+                translations['message'] = utils.build_underscore_name (item.fullname)
+                translations['cid']     = item.cid_enum_name
+                inner_template = (
+                    '    [${cid}] = {\n')
+                if item.has_query:
+                    inner_template += (
+                        '        .query_cb = ${message}_query_get_printable,\n')
+                if item.has_set:
+                    inner_template += (
+                        '        .set_cb = ${message}_set_get_printable,\n')
+                if item.has_response:
+                    inner_template += (
+                        '        .response_cb = ${message}_response_get_printable,\n')
+                if item.has_notification:
+                    inner_template += (
+                        '        .notification_cb = ${message}_notification_get_printable,\n')
                 inner_template += (
-                    '        .query_cb = ${message}_query_get_printable,\n')
-            if item.has_set:
-                inner_template += (
-                    '        .set_cb = ${message}_set_get_printable,\n')
-            if item.has_response:
-                inner_template += (
-                    '        .response_cb = ${message}_response_get_printable,\n')
-            if item.has_notification:
-                inner_template += (
-                    '        .notification_cb = ${message}_notification_get_printable,\n')
-            inner_template += (
-                '    },\n')
-            template += (string.Template(inner_template).substitute(translations))
+                    '    },\n')
+                template += (string.Template(inner_template).substitute(translations))
 
         template += (
             '};\n'
@@ -146,15 +152,15 @@
             '    switch (mbim_message_get_message_type (message)) {\n'
             '        case MBIM_MESSAGE_TYPE_COMMAND: {\n'
             '            cid = mbim_message_command_get_cid (message);\n'
-            '            if (cid < G_N_ELEMENTS (get_printable_callbacks)) {\n'
+            '            if (cid < G_N_ELEMENTS (${service_underscore}_get_printable_callbacks)) {\n'
             '                switch (mbim_message_command_get_command_type (message)) {\n'
             '                    case MBIM_MESSAGE_COMMAND_TYPE_QUERY:\n'
-            '                        if (get_printable_callbacks[cid].query_cb)\n'
-            '                            return get_printable_callbacks[cid].query_cb (message, line_prefix, error);\n'
+            '                        if (${service_underscore}_get_printable_callbacks[cid].query_cb)\n'
+            '                            return ${service_underscore}_get_printable_callbacks[cid].query_cb (message, line_prefix, error);\n'
             '                        break;\n'
             '                    case MBIM_MESSAGE_COMMAND_TYPE_SET:\n'
-            '                        if (get_printable_callbacks[cid].set_cb)\n'
-            '                            return get_printable_callbacks[cid].set_cb (message, line_prefix, error);\n'
+            '                        if (${service_underscore}_get_printable_callbacks[cid].set_cb)\n'
+            '                            return ${service_underscore}_get_printable_callbacks[cid].set_cb (message, line_prefix, error);\n'
             '                        break;\n'
             '                    case MBIM_MESSAGE_COMMAND_TYPE_UNKNOWN:\n'
             '                    default:\n'
@@ -170,17 +176,17 @@
             '\n'
             '        case MBIM_MESSAGE_TYPE_COMMAND_DONE:\n'
             '            cid = mbim_message_command_done_get_cid (message);\n'
-            '            if (cid < G_N_ELEMENTS (get_printable_callbacks)) {\n'
-            '                if (get_printable_callbacks[cid].response_cb)\n'
-            '                    return get_printable_callbacks[cid].response_cb (message, line_prefix, error);\n'
+            '            if (cid < G_N_ELEMENTS (${service_underscore}_get_printable_callbacks)) {\n'
+            '                if (${service_underscore}_get_printable_callbacks[cid].response_cb)\n'
+            '                    return ${service_underscore}_get_printable_callbacks[cid].response_cb (message, line_prefix, error);\n'
             '            }\n'
             '            break;\n'
             '\n'
             '        case MBIM_MESSAGE_TYPE_INDICATE_STATUS:\n'
             '            cid = mbim_message_indicate_status_get_cid (message);\n'
-            '            if (cid < G_N_ELEMENTS (get_printable_callbacks)) {\n'
-            '                if (get_printable_callbacks[cid].notification_cb)\n'
-            '                    return get_printable_callbacks[cid].notification_cb (message, line_prefix, error);\n'
+            '            if (cid < G_N_ELEMENTS (${service_underscore}_get_printable_callbacks)) {\n'
+            '                if (${service_underscore}_get_printable_callbacks[cid].notification_cb)\n'
+            '                    return ${service_underscore}_get_printable_callbacks[cid].notification_cb (message, line_prefix, error);\n'
             '            }\n'
             '            break;\n'
             '\n'
@@ -201,20 +207,48 @@
             '\n'
             '    g_set_error (error,\n'
             '                 MBIM_CORE_ERROR,\n'
-            '                 MBIM_CORE_ERROR_INVALID_MESSAGE,\n'
-            '                 \"Unknown contents\");\n'
+            '                 MBIM_CORE_ERROR_UNSUPPORTED,\n'
+            '                 \"Unsupported message\");\n'
             '    return NULL;\n'
             '}\n')
 
         cfile.write(string.Template(template).substitute(translations))
 
+    def emit_printable(self, hfile, cfile):
+
+        template = (
+            '\n'
+            '/*****************************************************************************/\n'
+            '/* Service helpers for printable fields */\n'
+            '\n'
+            '#if defined (LIBMBIM_GLIB_COMPILATION)\n')
+        hfile.write(template)
+
+        template = (
+            '\n'
+            'typedef struct {\n'
+            '  gchar * (* query_cb)        (const MbimMessage *message, const gchar *line_prefix, GError **error);\n'
+            '  gchar * (* set_cb)          (const MbimMessage *message, const gchar *line_prefix, GError **error);\n'
+            '  gchar * (* response_cb)     (const MbimMessage *message, const gchar *line_prefix, GError **error);\n'
+            '  gchar * (* notification_cb) (const MbimMessage *message, const gchar *line_prefix, GError **error);\n'
+            '} GetPrintableCallbacks;\n')
+        cfile.write(template)
+
+        for service in self.service_list:
+            self.emit_printable_service(hfile, cfile, service)
+
+        template = (
+            '\n'
+            '#endif\n')
+        hfile.write(template)
+
 
     """
-    Emit the sections
+    Emit the section for a single service
     """
-    def emit_sections(self, sfile):
-        translations = { 'service_dashed' : utils.build_dashed_name(self.service),
-                         'service'        : self.service }
+    def emit_sections_service(self, sfile, service):
+        translations = { 'service_dashed' : utils.build_dashed_name(service),
+                         'service'        : service }
 
         # Emit section header
         template = (
@@ -234,3 +268,7 @@
 
         sfile.write(
             '</SECTION>\n')
+
+    def emit_sections(self, sfile):
+        for service in self.service_list:
+            self.emit_sections_service(sfile, service)
diff --git a/build-aux/mbim-codegen/Struct.py b/build-aux/mbim-codegen/Struct.py
index 445e175..02037a5 100644
--- a/build-aux/mbim-codegen/Struct.py
+++ b/build-aux/mbim-codegen/Struct.py
@@ -26,12 +26,19 @@
         # Whether the struct is used as a single field, or as an array of
         # fields. Will be updated after having created the object.
         self.single_member = False
-        self.array_member = False
+        self.ms_struct = False
+        self.ref_struct_array_member = False
+        self.struct_array_member = False
+        self.ms_struct_array_member = False
 
         # Check whether the struct is composed of fixed-sized fields
         self.size = 0
         for field in self.contents:
-            if field['format'] == 'guint32':
+            if field['format'] == 'guint16':
+                self.size += 2
+            elif field['format'] == 'guint32':
+                self.size += 4
+            elif field['format'] == 'gint32':
                 self.size += 4
             elif field['format'] == 'guint64':
                 self.size += 8
@@ -68,15 +75,36 @@
                 if 'array-size-field' not in field:
                     inner_template += (' * @${field_name_underscore}_size: size of the ${field_name_underscore} array.\n')
                 inner_template += (' * @${field_name_underscore}: an array of #guint8 values.\n')
+            elif field['format'] == 'guint16':
+                if 'public-format' in field:
+                    translations['public'] = field['public-format']
+                    inner_template = (
+                        ' * @${field_name_underscore}: a ${public} given as a #guint16.\n')
+                else:
+                    inner_template = (
+                        ' * @${field_name_underscore}: a #guint16.\n')
             elif field['format'] == 'guint32':
+                if 'public-format' in field:
+                    translations['public'] = field['public-format']
+                    inner_template = (
+                        ' * @${field_name_underscore}: a ${public} given as a #guint32.\n')
+                else:
+                    inner_template = (
+                        ' * @${field_name_underscore}: a #guint32.\n')
+            elif field['format'] == 'gint32':
                 inner_template = (
-                    ' * @${field_name_underscore}: a #guint32.\n')
+                    ' * @${field_name_underscore}: a #gint32.\n')
             elif field['format'] == 'guint32-array':
                 inner_template = (
                     ' * @${field_name_underscore}: an array of #guint32 values.\n')
             elif field['format'] == 'guint64':
-                inner_template = (
-                    ' * @${field_name_underscore}: a #guint64.\n')
+                if 'public-format' in field:
+                    translations['public'] = field['public-format']
+                    inner_template = (
+                        ' * @${field_name_underscore}: a ${public} given as a #guint64.\n')
+                else:
+                    inner_template = (
+                        ' * @${field_name_underscore}: a #guint64.\n')
             elif field['format'] == 'string':
                 inner_template = (
                     ' * @${field_name_underscore}: a string.\n')
@@ -120,9 +148,15 @@
                         '    guint32 ${field_name_underscore}_size;\n')
                 inner_template += (
                     '    guint8 *${field_name_underscore};\n')
+            elif field['format'] == 'guint16':
+                inner_template = (
+                    '    guint16 ${field_name_underscore};\n')
             elif field['format'] == 'guint32':
                 inner_template = (
                     '    guint32 ${field_name_underscore};\n')
+            elif field['format'] == 'gint32':
+                inner_template = (
+                    '    gint32 ${field_name_underscore};\n')
             elif field['format'] == 'guint32-array':
                 inner_template = (
                     '    guint32 *${field_name_underscore};\n')
@@ -197,8 +231,12 @@
             elif field['format'] in ['unsized-byte-array', 'ref-byte-array', 'ref-byte-array-no-offset']:
                 inner_template += (
                     '    g_free (var->${field_name_underscore});\n')
+            elif field['format'] == 'guint16':
+                pass
             elif field['format'] == 'guint32':
                 pass
+            elif field['format'] == 'gint32':
+                pass
             elif field['format'] == 'guint32-array':
                 inner_template += (
                     '    g_free (var->${field_name_underscore});\n')
@@ -237,7 +275,7 @@
                 '}\n')
             cfile.write(string.Template(template).substitute(translations))
 
-        if self.array_member:
+        if self.struct_array_member == True or self.ref_struct_array_member == True or self.ms_struct_array_member == True:
             # TypeArray was introduced in 1.24
             translations['array_since'] = self.since if utils.version_compare('1.24', self.since) > 0 else '1.24'
             template = (
@@ -299,8 +337,11 @@
             '\n')
 
         for field in self.contents:
-            translations['field_name'] = field['name']
-            translations['field_name_underscore'] = utils.build_underscore_name_from_camelcase(field['name'])
+            translations['field_name']              = field['name']
+            translations['field_name_underscore']   = utils.build_underscore_name_from_camelcase(field['name'])
+            translations['public']                  = field['public-format'] if 'public-format' in field else field['format']
+            translations['public_underscore']       = utils.build_underscore_name_from_camelcase(field['public-format']) if 'public-format' in field else ''
+            translations['public_underscore_upper'] = utils.build_underscore_name_from_camelcase(field['public-format']).upper() if 'public-format' in field else ''
 
             inner_template = (
                 '    g_string_append_printf (str, "%s  ${field_name} = ", line_prefix);\n'
@@ -338,14 +379,34 @@
                     '            g_string_append_printf (str, "%02x%s", self->${field_name_underscore}[i], (i == (array_size - 1)) ? "" : ":" );\n'
                     '        g_string_append (str, "\'");\n')
 
-            elif field['format'] == 'guint32':
-                inner_template += (
-                    '        g_string_append_printf (str, "\'%" G_GUINT32_FORMAT "\'", self->${field_name_underscore});\n')
-
-            elif field['format'] == 'guint64':
-                inner_template += (
-                    '        g_string_append_printf (str, "\'%" G_GUINT64_FORMAT "\'", self->${field_name_underscore});\n')
-
+            elif field['format'] in ['guint16', 'guint32', 'guint64']:
+                if 'public-format' in field:
+                    inner_template += (
+                        '#if defined __${public_underscore_upper}_IS_ENUM__\n'
+                        '        g_string_append_printf (str, "\'%s\'", ${public_underscore}_get_string ((${public})self->${field_name_underscore}));\n'
+                        '#elif defined __${public_underscore_upper}_IS_FLAGS__\n'
+                        '        {\n'
+                        '            g_autofree gchar *tmpstr = NULL;\n'
+                        '\n'
+                        '            tmpstr = ${public_underscore}_build_string_from_mask ((${public})self->${field_name_underscore});\n'
+                        '            g_string_append_printf (str, "\'%s\'", tmpstr);\n'
+                        '        }\n'
+                        '#else\n'
+                        '# error neither enum nor flags\n'
+                        '#endif\n'
+                        '\n')
+                elif field['format'] == 'guint16':
+                    inner_template += (
+                        '        g_string_append_printf (str, "\'%" G_GUINT16_FORMAT "\'", self->${field_name_underscore});\n')
+                elif field['format'] == 'guint32':
+                    inner_template += (
+                        '        g_string_append_printf (str, "\'%" G_GUINT32_FORMAT "\'", self->${field_name_underscore});\n')
+                elif field['format'] == 'guint64':
+                    inner_template += (
+                        '        g_string_append_printf (str, "\'%" G_GUINT64_FORMAT "\'", self->${field_name_underscore});\n')
+            elif field['format'] == 'gint32':
+                    inner_template += (
+                        '        g_string_append_printf (str, "\'%" G_GINT32_FORMAT "\'", self->${field_name_underscore});\n')
             elif field['format'] == 'guint32-array':
                 translations['array_size_field_name_underscore'] = utils.build_underscore_name_from_camelcase(field['array-size-field'])
                 inner_template += (
@@ -428,7 +489,9 @@
             '\n'
             '    g_assert (self != NULL);\n'
             '\n'
-            '    out = g_new0 (${name}, 1);\n')
+            '    out = g_new0 (${name}, 1);\n'
+            '\n')
+
 
         for field in self.contents:
             translations['field_name_underscore'] = utils.build_underscore_name_from_camelcase(field['name'])
@@ -496,12 +559,24 @@
                     '        memcpy (out->${field_name_underscore}, tmp, ${array_size});\n'
                     '        offset += ${array_size};\n'
                     '    }\n')
+            elif field['format'] == 'guint16':
+                inner_template += (
+                    '\n'
+                    '    if (!_mbim_message_read_guint16 (self, offset, &out->${field_name_underscore}, error))\n'
+                    '        goto out;\n'
+                    '    offset += 2;\n')
             elif field['format'] == 'guint32':
                 inner_template += (
                     '\n'
                     '    if (!_mbim_message_read_guint32 (self, offset, &out->${field_name_underscore}, error))\n'
                     '        goto out;\n'
                     '    offset += 4;\n')
+            elif field['format'] == 'gint32':
+                inner_template += (
+                    '\n'
+                    '    if (!_mbim_message_read_gint32 (self, offset, &out->${field_name_underscore}, error))\n'
+                    '        goto out;\n'
+                    '    offset += 4;\n')
             elif field['format'] == 'guint32-array':
                 translations['array_size_field_name_underscore'] = utils.build_underscore_name_from_camelcase(field['array-size-field'])
                 inner_template += (
@@ -604,7 +679,44 @@
             '}\n')
         cfile.write(string.Template(template).substitute(translations))
 
-        if self.array_member:
+        if self.ms_struct == True:
+            template = (
+                '\n'
+                'static gboolean\n'
+                '_mbim_message_read_${name_underscore}_ms_struct (\n'
+                '    const MbimMessage *self,\n'
+                '    guint32 relative_offset,\n'
+                '    ${name} **out_struct,\n'
+                '    GError **error)\n'
+                '{\n'
+                '    ${name} *out;\n'
+                '    guint32 offset;\n'
+                '    guint32 size;\n'
+                '\n'
+                '    g_assert (self != NULL);\n'
+                '\n'
+                '    if (!_mbim_message_read_guint32 (self, relative_offset, &offset, error))\n'
+                '        return FALSE;\n'
+                '    relative_offset += 4;\n'
+                '\n'
+                '    if (!_mbim_message_read_guint32 (self, relative_offset, &size, error))\n'
+                '        return FALSE;\n'
+                '    relative_offset += 4;\n'
+                '\n'
+                '    if (!offset) {\n'
+                '        *out_struct = NULL;\n'
+                '        return TRUE;\n'
+                '    }\n'
+                '\n'
+                '    out = _mbim_message_read_${name_underscore}_struct (self, offset, NULL, error);\n'
+                '    if (!out)\n'
+                '        return FALSE;\n'
+                '    *out_struct = out;\n'
+                '    return TRUE;\n'
+                '}\n')
+            cfile.write(string.Template(template).substitute(translations))
+
+        if self.struct_array_member == True:
             template = (
                 '\n'
                 'static gboolean\n'
@@ -612,7 +724,6 @@
                 '    const MbimMessage *self,\n'
                 '    guint32 array_size,\n'
                 '    guint32 relative_offset_array_start,\n'
-                '    gboolean refs,\n'
                 '    ${name}Array **out_array,\n'
                 '    GError **error)\n'
                 '{\n'
@@ -628,18 +739,50 @@
                 '\n'
                 '    out = g_new0 (${name} *, array_size + 1);\n'
                 '\n'
-                '    if (!refs) {\n'
-                '        _mbim_message_read_guint32 (self, relative_offset_array_start, &offset, &inner_error);\n'
-                '        for (i = 0; !inner_error && (i < array_size); i++, offset += ${struct_size})\n'
-                '            out[i] = _mbim_message_read_${name_underscore}_struct (self, offset, NULL, &inner_error);\n'
-                '    } else {\n'
-                '        offset = relative_offset_array_start;\n'
-                '        for (i = 0; !inner_error && (i < array_size); i++, offset += 8) {\n'
-                '            guint32 tmp_offset;\n'
+                '    _mbim_message_read_guint32 (self, relative_offset_array_start, &offset, &inner_error);\n'
+                '    for (i = 0; !inner_error && (i < array_size); i++, offset += ${struct_size})\n'
+                '        out[i] = _mbim_message_read_${name_underscore}_struct (self, offset, NULL, &inner_error);\n'
                 '\n'
-                '            if (_mbim_message_read_guint32 (self, offset, &tmp_offset, &inner_error))\n'
-                '                out[i] = _mbim_message_read_${name_underscore}_struct (self, tmp_offset, NULL, &inner_error);\n'
-                '        }\n'
+                '    if (!inner_error) {\n'
+                '        *out_array = out;\n'
+                '        return TRUE;\n'
+                '    }\n'
+                '\n'
+                '    ${name_underscore}_array_free (out);\n'
+                '    g_propagate_error (error, inner_error);\n'
+                '    return FALSE;\n'
+                '}\n')
+            cfile.write(string.Template(template).substitute(translations))
+
+        if self.ref_struct_array_member == True:
+            template = (
+                '\n'
+                'static gboolean\n'
+                '_mbim_message_read_${name_underscore}_ref_struct_array (\n'
+                '    const MbimMessage *self,\n'
+                '    guint32 array_size,\n'
+                '    guint32 relative_offset_array_start,\n'
+                '    ${name}Array **out_array,\n'
+                '    GError **error)\n'
+                '{\n'
+                '    GError *inner_error = NULL;\n'
+                '    ${name}Array *out;\n'
+                '    guint32 i;\n'
+                '    guint32 offset;\n'
+                '\n'
+                '    if (!array_size) {\n'
+                '        *out_array = NULL;\n'
+                '        return TRUE;\n'
+                '    }\n'
+                '\n'
+                '    out = g_new0 (${name} *, array_size + 1);\n'
+                '\n'
+                '    offset = relative_offset_array_start;\n'
+                '    for (i = 0; !inner_error && (i < array_size); i++, offset += 8) {\n'
+                '        guint32 tmp_offset;\n'
+                '\n'
+                '        if (_mbim_message_read_guint32 (self, offset, &tmp_offset, &inner_error))\n'
+                '            out[i] = _mbim_message_read_${name_underscore}_struct (self, tmp_offset, NULL, &inner_error);\n'
                 '    }\n'
                 '\n'
                 '    if (!inner_error) {\n'
@@ -653,6 +796,66 @@
                 '}\n')
             cfile.write(string.Template(template).substitute(translations))
 
+        if self.ms_struct_array_member == True:
+            template = (
+                '\n'
+                'static gboolean\n'
+                '_mbim_message_read_${name_underscore}_ms_struct_array (\n'
+                '    const MbimMessage *self,\n'
+                '    guint32 offset,\n'
+                '    guint32 *out_array_size,\n'
+                '    ${name}Array **out_array,\n'
+                '    GError **error)\n'
+                '{\n'
+                '    GError *inner_error = NULL;\n'
+                '    ${name}Array *out;\n'
+                '    guint32 i;\n'
+                '    guint32 intermediate_struct_offset;\n'
+                '    guint32 intermediate_struct_size;\n'
+                '    guint32 array_size;\n'
+                '\n'
+                '    if (!_mbim_message_read_guint32 (self, offset, &intermediate_struct_offset, error))\n'
+                '        return FALSE;\n'
+                '    offset += 4;\n'
+                '\n'
+                '    if (!_mbim_message_read_guint32 (self, offset, &intermediate_struct_size, error))\n'
+                '        return FALSE;\n'
+                '    offset += 4;\n'
+                '\n'
+                '    if (!intermediate_struct_offset) {\n'
+                '        *out_array_size = 0;\n'
+                '        *out_array = NULL;\n'
+                '        return TRUE;\n'
+                '    }\n'
+                '\n'
+                '    if (!_mbim_message_read_guint32 (self, intermediate_struct_offset, &array_size, error))\n'
+                '        return FALSE;\n'
+                '\n'
+                '    if (!array_size) {\n'
+                '        *out_array_size = 0;\n'
+                '        *out_array = NULL;\n'
+                '        return TRUE;\n'
+                '    }\n'
+                '\n'
+                '    intermediate_struct_offset += 4;\n'
+                '\n'
+                '    out = g_new0 (${name} *, array_size + 1);\n'
+                '\n'
+                '    for (i = 0; !inner_error && (i < array_size); i++, intermediate_struct_offset += ${struct_size}) {\n'
+                '        out[i] = _mbim_message_read_${name_underscore}_struct (self, intermediate_struct_offset, NULL, &inner_error);\n'
+                '    }\n'
+                '\n'
+                '    if (!inner_error) {\n'
+                '        *out_array_size = array_size;\n'
+                '        *out_array = out;\n'
+                '        return TRUE;\n'
+                '    }\n'
+                '\n'
+                '    ${name_underscore}_array_free (out);\n'
+                '    g_propagate_error (error, inner_error);\n'
+                '    return FALSE;\n'
+                '}\n')
+            cfile.write(string.Template(template).substitute(translations))
 
     """
     Emit the type's append methods
@@ -691,8 +894,12 @@
                     inner_template = ('    _mbim_struct_builder_append_byte_array (builder, ${has_offset}, FALSE, ${pad_array}, value->${field}, value->${array_size_field}, FALSE);\n')
                 else:
                     inner_template = ('    _mbim_struct_builder_append_byte_array (builder, ${has_offset}, TRUE, ${pad_array}, value->${field}, value->${field}_size, FALSE);\n')
+            elif field['format'] == 'guint16':
+                inner_template = ('    _mbim_struct_builder_append_guint16 (builder, value->${field});\n')
             elif field['format'] == 'guint32':
                 inner_template = ('    _mbim_struct_builder_append_guint32 (builder, value->${field});\n')
+            elif field['format'] == 'gint32':
+                inner_template = ('    _mbim_struct_builder_append_gint32 (builder, value->${field});\n')
             elif field['format'] == 'guint32-array':
                 inner_template = ('    _mbim_struct_builder_append_guint32_array (builder, value->${field}, value->${array_size_field});\n')
             elif field['format'] == 'guint64':
@@ -743,93 +950,112 @@
             '}\n')
         cfile.write(string.Template(template).substitute(translations))
 
-        template = (
-            '\n'
-            'static void\n'
-            '_mbim_struct_builder_append_${name_underscore}_struct_array (\n'
-            '    MbimStructBuilder *builder,\n'
-            '    const ${name} *const *values,\n'
-            '    guint32 n_values,\n'
-            '    gboolean refs)\n'
-            '{\n'
-            '    guint32 offset;\n'
-            '    guint32 i;\n'
-            '    GByteArray *raw_all = NULL;\n'
-            '\n'
-            '    if (!refs) {\n'
-            '        for (i = 0; i < n_values; i++) {\n'
-            '            GByteArray *raw;\n'
-            '\n'
-            '            raw = _${name_underscore}_struct_new (values[i]);\n'
-            '            if (!raw_all)\n'
-            '                raw_all = raw;\n'
-            '            else {\n'
-            '                g_byte_array_append (raw_all, raw->data, raw->len);\n'
-            '                g_byte_array_unref (raw);\n'
-            '            }\n'
-            '        }\n'
-            '\n'
-            '        if (!raw_all) {\n'
-            '            offset = 0;\n'
-            '            g_byte_array_append (builder->fixed_buffer, (guint8 *)&offset, sizeof (offset));\n'
-            '        } else {\n'
-            '            guint32 offset_offset;\n'
-            '\n'
-            '            /* Offset of the offset */\n'
-            '            offset_offset = builder->fixed_buffer->len;\n'
-            '            /* Length *not* in LE yet */\n'
-            '            offset = builder->variable_buffer->len;\n'
-            '            /* Add the offset value */\n'
-            '            g_byte_array_append (builder->fixed_buffer, (guint8 *)&offset, sizeof (offset));\n'
-            '            /* Configure the value to get updated */\n'
-            '            g_array_append_val (builder->offsets, offset_offset);\n'
-            '            /* Add the final array itself */\n'
-            '            g_byte_array_append (builder->variable_buffer, raw_all->data, raw_all->len);\n'
-            '            g_byte_array_unref (raw_all);\n'
-            '        }\n'
-            '    } else {\n'
-            '        for (i = 0; i < n_values; i++) {\n'
-            '            guint32 length;\n'
-            '            guint32 offset_offset;\n'
-            '            GByteArray *raw;\n'
-            '\n'
-            '            raw = _${name_underscore}_struct_new (values[i]);\n'
-            '            g_assert (raw->len > 0);\n'
-            '\n'
-            '            /* Offset of the offset */\n'
-            '            offset_offset = builder->fixed_buffer->len;\n'
-            '\n'
-            '            /* Length *not* in LE yet */\n'
-            '            offset = builder->variable_buffer->len;\n'
-            '            /* Add the offset value */\n'
-            '            g_byte_array_append (builder->fixed_buffer, (guint8 *)&offset, sizeof (offset));\n'
-            '            /* Configure the value to get updated */\n'
-            '            g_array_append_val (builder->offsets, offset_offset);\n'
-            '\n'
-            '            /* Add the length value */\n'
-            '            length = GUINT32_TO_LE (raw->len);\n'
-            '            g_byte_array_append (builder->fixed_buffer, (guint8 *)&length, sizeof (length));\n'
-            '\n'
-            '            /* And finally, the bytearray itself to the variable buffer */\n'
-            '            g_byte_array_append (builder->variable_buffer, (const guint8 *)raw->data, (guint)raw->len);\n'
-            '            g_byte_array_unref (raw);\n'
-            '        }\n'
-            '    }\n'
-            '}\n')
-        cfile.write(string.Template(template).substitute(translations))
+        if self.struct_array_member == True:
+            template = (
+                '\n'
+                'static void\n'
+                '_mbim_struct_builder_append_${name_underscore}_struct_array (\n'
+                '    MbimStructBuilder *builder,\n'
+                '    const ${name} *const *values,\n'
+                '    guint32 n_values)\n'
+                '{\n'
+                '    guint32 offset;\n'
+                '    guint32 i;\n'
+                '    GByteArray *raw_all = NULL;\n'
+                '\n'
+                '    for (i = 0; i < n_values; i++) {\n'
+                '        GByteArray *raw;\n'
+                '\n'
+                '        raw = _${name_underscore}_struct_new (values[i]);\n'
+                '        if (!raw_all)\n'
+                '            raw_all = raw;\n'
+                '        else {\n'
+                '            g_byte_array_append (raw_all, raw->data, raw->len);\n'
+                '            g_byte_array_unref (raw);\n'
+                '        }\n'
+                '    }\n'
+                '\n'
+                '    if (!raw_all) {\n'
+                '        offset = 0;\n'
+                '        g_byte_array_append (builder->fixed_buffer, (guint8 *)&offset, sizeof (offset));\n'
+                '    } else {\n'
+                '        guint32 offset_offset;\n'
+                '\n'
+                '        /* Offset of the offset */\n'
+                '        offset_offset = builder->fixed_buffer->len;\n'
+                '        /* Length *not* in LE yet */\n'
+                '        offset = builder->variable_buffer->len;\n'
+                '        /* Add the offset value */\n'
+                '        g_byte_array_append (builder->fixed_buffer, (guint8 *)&offset, sizeof (offset));\n'
+                '        /* Configure the value to get updated */\n'
+                '        g_array_append_val (builder->offsets, offset_offset);\n'
+                '        /* Add the final array itself */\n'
+                '        g_byte_array_append (builder->variable_buffer, raw_all->data, raw_all->len);\n'
+                '        g_byte_array_unref (raw_all);\n'
+                '    }\n'
+                '}\n'
+                '\n'
+                'static void\n'
+                '_mbim_message_command_builder_append_${name_underscore}_struct_array (\n'
+                '    MbimMessageCommandBuilder *builder,\n'
+                '    const ${name} *const *values,\n'
+                '    guint32 n_values)\n'
+                '{\n'
+                '    _mbim_struct_builder_append_${name_underscore}_struct_array (builder->contents_builder, values, n_values);\n'
+                '}\n')
+            cfile.write(string.Template(template).substitute(translations))
 
-        template = (
-            '\n'
-            'static void\n'
-            '_mbim_message_command_builder_append_${name_underscore}_struct_array (\n'
-            '    MbimMessageCommandBuilder *builder,\n'
-            '    const ${name} *const *values,\n'
-            '    guint32 n_values,\n'
-            '    gboolean refs)\n'
-            '{\n'
-            '    _mbim_struct_builder_append_${name_underscore}_struct_array (builder->contents_builder, values, n_values, refs);\n'
-            '}\n')
-        cfile.write(string.Template(template).substitute(translations))
+        if self.ref_struct_array_member == True:
+            template = (
+                '\n'
+                'static void\n'
+                '_mbim_struct_builder_append_${name_underscore}_ref_struct_array (\n'
+                '    MbimStructBuilder *builder,\n'
+                '    const ${name} *const *values,\n'
+                '    guint32 n_values)\n'
+                '{\n'
+                '    guint32 offset;\n'
+                '    guint32 i;\n'
+                '\n'
+                '    for (i = 0; i < n_values; i++) {\n'
+                '        guint32 length;\n'
+                '        guint32 offset_offset;\n'
+                '        GByteArray *raw;\n'
+                '\n'
+                '        raw = _${name_underscore}_struct_new (values[i]);\n'
+                '        g_assert (raw->len > 0);\n'
+                '\n'
+                '        /* Offset of the offset */\n'
+                '        offset_offset = builder->fixed_buffer->len;\n'
+                '\n'
+                '        /* Length *not* in LE yet */\n'
+                '        offset = builder->variable_buffer->len;\n'
+                '        /* Add the offset value */\n'
+                '        g_byte_array_append (builder->fixed_buffer, (guint8 *)&offset, sizeof (offset));\n'
+                '        /* Configure the value to get updated */\n'
+                '        g_array_append_val (builder->offsets, offset_offset);\n'
+                '\n'
+                '        /* Add the length value */\n'
+                '        length = GUINT32_TO_LE (raw->len);\n'
+                '        g_byte_array_append (builder->fixed_buffer, (guint8 *)&length, sizeof (length));\n'
+                '\n'
+                '        /* And finally, the bytearray itself to the variable buffer */\n'
+                '        g_byte_array_append (builder->variable_buffer, (const guint8 *)raw->data, (guint)raw->len);\n'
+                '        g_byte_array_unref (raw);\n'
+                '    }\n'
+                '}\n'
+                '\n'
+                'static void\n'
+                '_mbim_message_command_builder_append_${name_underscore}_ref_struct_array (\n'
+                '    MbimMessageCommandBuilder *builder,\n'
+                '    const ${name} *const *values,\n'
+                '    guint32 n_values)\n'
+                '{\n'
+                '    _mbim_struct_builder_append_${name_underscore}_ref_struct_array (builder->contents_builder, values, n_values);\n'
+                '}\n')
+            cfile.write(string.Template(template).substitute(translations))
+
+        # append operations not implemented for self.ms_struct_array_member == True
 
 
     """
@@ -860,13 +1086,13 @@
         template = (
             '<SUBSECTION ${struct_name}>\n'
             '${struct_name}\n')
-        if self.array_member == True:
+        if self.struct_array_member == True or self.ref_struct_array_member == True:
             template += (
                 '${struct_name}Array\n')
         if self.single_member == True:
             template += (
                 '${name_underscore}_free\n')
-        if self.array_member == True:
+        if self.struct_array_member == True or self.ref_struct_array_member == True:
             template += (
                 '${name_underscore}_array_free\n')
         sfile.write(string.Template(template).substitute(translations))
diff --git a/build-aux/mbim-codegen/mbim-codegen b/build-aux/mbim-codegen/mbim-codegen
index 4ca6b38..4d7c4ad 100755
--- a/build-aux/mbim-codegen/mbim-codegen
+++ b/build-aux/mbim-codegen/mbim-codegen
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 #
 # SPDX-License-Identifier: LGPL-2.1-or-later
@@ -17,13 +17,11 @@
 def codegen_main():
     # Input arguments
     arg_parser = optparse.OptionParser('%prog [options]')
-    arg_parser.add_option('', '--input', metavar='JSONFILE',
-                          help='Input JSON-formatted database')
     arg_parser.add_option('', '--output', metavar='OUTFILES',
                           help='Generate C code in OUTFILES.[ch]')
     (opts, args) = arg_parser.parse_args();
 
-    if opts.input == None:
+    if args == None:
         raise RuntimeError('Input JSON file is mandatory')
     if opts.output == None:
         raise RuntimeError('Output file pattern is mandatory')
@@ -33,11 +31,11 @@
     output_file_h = open(opts.output + ".h", 'w')
     output_file_sections = open(opts.output + ".sections", 'w')
 
-    # Load database file contents
-    database_file_contents = utils.read_json_file(opts.input)
-
-    # Build message list
-    object_list_json = json.loads(database_file_contents)
+    # Build message list from all input files
+    object_list_json = []
+    for input_file in args:
+        database_file_contents = utils.read_json_file(input_file)
+        object_list_json += json.loads(database_file_contents)
     object_list = ObjectList(object_list_json)
 
     # Add common stuff to the output files
diff --git a/build-aux/mbim-codegen/utils.py b/build-aux/mbim-codegen/utils.py
index c3a8ac3..6722269 100644
--- a/build-aux/mbim-codegen/utils.py
+++ b/build-aux/mbim-codegen/utils.py
@@ -46,6 +46,7 @@
         "#include \"mbim-message.h\"\n"
         "#include \"mbim-device.h\"\n"
         "#include \"mbim-enums.h\"\n"
+        "#include \"mbim-tlv.h\"\n"
         "\n"
         "#ifndef ${guard}\n"
         "#define ${guard}\n"
@@ -76,6 +77,7 @@
         "\n"
         "#include \"${name}.h\"\n"
         "#include \"mbim-message-private.h\"\n"
+        "#include \"mbim-tlv-private.h\"\n"
         "#include \"mbim-enum-types.h\"\n"
         "#include \"mbim-error-types.h\"\n"
         "#include \"mbim-device.h\"\n"
diff --git a/data/mbim-service-atds.json b/data/mbim-service-atds.json
index 3b8f2ed..717f537 100644
--- a/data/mbim-service-atds.json
+++ b/data/mbim-service-atds.json
@@ -5,7 +5,6 @@
 
   // *********************************************************************************
   { "name"         : "Signal",
-    "service"      : "ATDS",
     "type"         : "Command",
     "since"        : "1.16",
     "query"        : [],
@@ -64,7 +63,6 @@
 
   // *********************************************************************************
   { "name"         : "Location",
-    "service"      : "ATDS",
     "type"         : "Command",
     "since"        : "1.16",
     "query"        : [],
@@ -95,7 +93,6 @@
                      "format"        : "guint32" } ] },
 
   { "name"     : "Operators",
-    "service"  : "ATDS",
     "type"     : "Command",
     "since"    : "1.16",
     "query"    : [],
@@ -108,7 +105,6 @@
 
   // *********************************************************************************
   { "name"     : "RAT",
-    "service"  : "ATDS",
     "type"     : "Command",
     "since"    : "1.16",
     "set"      : [ { "name"          : "Mode",
@@ -121,7 +117,6 @@
 
   // *********************************************************************************
   { "name"         : "Register State",
-    "service"      : "ATDS",
     "type"         : "Command",
     "since"        : "1.16",
     "query"        : [],
diff --git a/data/mbim-service-auth.json b/data/mbim-service-auth.json
index ea8a783..3f099ef 100644
--- a/data/mbim-service-auth.json
+++ b/data/mbim-service-auth.json
@@ -6,7 +6,6 @@
 
   // *********************************************************************************
   { "name"     : "Aka",
-    "service"  : "Auth",
     "type"     : "Command",
     "since"    : "1.4",
     "query"    : [ { "name"       : "Rand",
@@ -32,7 +31,6 @@
 
   // *********************************************************************************
   { "name"     : "Akap",
-    "service"  : "Auth",
     "type"     : "Command",
     "since"    : "1.4",
     "query"    : [ { "name"       : "Rand",
@@ -60,7 +58,6 @@
 
   // *********************************************************************************
   { "name"     : "Sim",
-    "service"  : "Auth",
     "type"     : "Command",
     "since"    : "1.4",
     "query"    : [ { "name"       : "Rand1",
diff --git a/data/mbim-service-basic-connect.json b/data/mbim-service-basic-connect.json
index c664a79..6a45143 100644
--- a/data/mbim-service-basic-connect.json
+++ b/data/mbim-service-basic-connect.json
@@ -6,7 +6,6 @@
 
   // *********************************************************************************
   { "name"     : "Device Caps",
-    "service"  : "Basic Connect",
     "type"     : "Command",
     "since"    : "1.0",
     "query"    : [],
@@ -48,7 +47,6 @@
 
   // *********************************************************************************
   { "name"         : "Subscriber Ready Status",
-    "service"      : "Basic Connect",
     "type"         : "Command",
     "since-ex"     : { "query"        : "1.0",
                        "response"     : "1.0",
@@ -87,7 +85,6 @@
 
   // *********************************************************************************
   { "name"         : "Radio State",
-    "service"      : "Basic Connect",
     "type"         : "Command",
     "since"        : "1.0",
     "set"          : [ { "name"          : "RadioState",
@@ -109,7 +106,6 @@
 
   // *********************************************************************************
   { "name"     : "Pin",
-    "service"  : "Basic Connect",
     "type"     : "Command",
     "since"    : "1.0",
     "set"      : [ { "name"          : "PinType",
@@ -148,7 +144,6 @@
                      "format" : "guint32" } ] },
 
   { "name"     : "Pin List",
-    "service"  : "Basic Connect",
     "type"     : "Command",
     "since"    : "1.0",
     "query"    : [],
@@ -203,7 +198,6 @@
                      "format" : "guint32" } ] },
 
   { "name"     : "Home Provider",
-    "service"  : "Basic Connect",
     "type"     : "Command",
     "since"    : "1.2",
     "set"      : [ { "name"        : "Provider",
@@ -216,7 +210,6 @@
 
   // *********************************************************************************
   { "name"         : "Preferred Providers",
-    "service"      : "Basic Connect",
     "type"         : "Command",
     "since-ex"     : { "set"          : "1.2",
                        "query"        : "1.2",
@@ -244,7 +237,6 @@
 
   // *********************************************************************************
   { "name"       : "Visible Providers",
-    "service"    : "Basic Connect",
     "type"       : "Command",
     "since"      : "1.2",
     "query"      : [ { "name" : "Action",
@@ -259,7 +251,6 @@
 
   // *********************************************************************************
   { "name"         : "Register State",
-    "service"      : "Basic Connect",
     "type"         : "Command",
     "since"        : "1.0",
     "set"          : [ { "name"   : "ProviderId",
@@ -322,7 +313,6 @@
 
   // *********************************************************************************
   { "name"         : "Signal State",
-    "service"      : "Basic Connect",
     "type"         : "Command",
     "since"        : "1.0",
     "set"          : [ { "name"   : "SignalStrengthInterval",
@@ -356,7 +346,6 @@
 
   // *********************************************************************************
   { "name"         : "Packet Service",
-    "service"      : "Basic Connect",
     "type"         : "Command",
     "since"        : "1.0",
     "set"          : [ { "name"          : "PacketServiceAction",
@@ -390,7 +379,6 @@
 
   // *********************************************************************************
   { "name"         : "Connect",
-    "service"      : "Basic Connect",
     "type"         : "Command",
     "since"        : "1.0",
     "set"          : [ { "name"   : "SessionId",
@@ -483,7 +471,6 @@
                      "public-format" : "MbimAuthProtocol" } ] },
 
   { "name"         : "Provisioned Contexts",
-    "service"      : "Basic Connect",
     "type"         : "Command",
     "since"        : "1.0",
     "set"          : [ { "name"   : "ContextId",
@@ -521,7 +508,6 @@
   // *********************************************************************************
 
   { "name"     : "Service Activation",
-    "service"  : "Basic Connect",
     "type"     : "Command",
     "since"    : "1.2",
     "set"      : [ { "name"   : "Buffer",
@@ -550,7 +536,6 @@
                      "format" : "ipv6" } ] },
 
   { "name"         : "IP Configuration",
-    "service"      : "Basic Connect",
     "type"         : "Command",
     "since"        : "1.0",
     "query"        : [ { "name"   : "SessionId",
@@ -685,7 +670,6 @@
                      "array-size-field" : "CidsCount" } ] },
 
   { "name"     : "Device Services",
-    "service"  : "Basic Connect",
     "type"     : "Command",
     "since"    : "1.0",
     "query"    : [],
@@ -711,7 +695,6 @@
                      "array-size-field" : "CidsCount" } ] },
 
   { "name"     : "Device Service Subscribe List",
-    "service"  : "Basic Connect",
     "type"     : "Command",
     "since"    : "1.8",
     "set"      : [ { "name"   : "EventsCount",
@@ -729,7 +712,6 @@
 
   // *********************************************************************************
   { "name"     : "Packet Statistics",
-    "service"  : "Basic Connect",
     "type"     : "Command",
     "since"    : "1.4",
     "query"    : [],
@@ -752,7 +734,6 @@
 
   // *********************************************************************************
   { "name"     : "Network Idle Hint",
-    "service"  : "Basic Connect",
     "type"     : "Command",
     "since"    : "1.4",
     "query"    : [],
@@ -765,7 +746,6 @@
 
   // *********************************************************************************
   { "name"         : "Emergency Mode",
-    "service"      : "Basic Connect",
     "type"         : "Command",
     "since-ex"     : { "set"          : "1.4",
                        "query"        : "1.4",
@@ -796,7 +776,6 @@
                      "array-size-field" : "FilterSize" } ] },
 
   { "name"     : "IP Packet Filters",
-    "service"  : "Basic Connect",
     "type"     : "Command",
     "since"    : "1.4",
     "query"    : [ { "name"   : "SessionId",
@@ -826,7 +805,6 @@
 
   // *********************************************************************************
   { "name"         : "Multicarrier Providers",
-    "service"      : "Basic Connect",
     "type"         : "Command",
     "since-ex"     : { "set"          : "1.4",
                        "query"        : "1.4",
diff --git a/data/mbim-service-dss.json b/data/mbim-service-dss.json
index db72d17..93211fb 100644
--- a/data/mbim-service-dss.json
+++ b/data/mbim-service-dss.json
@@ -6,7 +6,6 @@
 
   // *********************************************************************************
   { "name"     : "Connect",
-    "service"  : "DSS",
     "type"     : "Command",
     "since"    : "1.4",
     "set"      : [ { "name"   : "DeviceServiceId",
diff --git a/data/mbim-service-intel-firmware-update.json b/data/mbim-service-intel-firmware-update.json
index 2998de1..e75af8d 100644
--- a/data/mbim-service-intel-firmware-update.json
+++ b/data/mbim-service-intel-firmware-update.json
@@ -5,7 +5,6 @@
 
   // *********************************************************************************
   { "name"     : "Modem Reboot",
-    "service"  : "Intel Firmware Update",
     "type"     : "Command",
     "since"    : "1.16",
     "set"      : [] }
diff --git a/data/mbim-service-ms-basic-connect-extensions-v2.json b/data/mbim-service-ms-basic-connect-extensions-v2.json
new file mode 100644
index 0000000..d06d9c8
--- /dev/null
+++ b/data/mbim-service-ms-basic-connect-extensions-v2.json
@@ -0,0 +1,21 @@
+[
+  // *********************************************************************************
+  { "type" : "Service",
+    "name" : "Ms Basic Connect Extensions V2",
+    "mbimex-service" : "Ms Basic Connect Extensions",
+    "mbimex-version" : "2.0" },
+
+  // *********************************************************************************
+
+  { "name"       : "Version",
+    "type"       : "Command",
+    "since"      : "1.28",
+    "query"      : [ { "name"   : "MbimVersion",
+                       "format" : "guint16" },
+                     { "name"   : "MbimExtendedVersion",
+                       "format" : "guint16" } ],
+    "response"   : [ { "name"   : "MbimVersion",
+                       "format" : "guint16" },
+                     { "name"   : "MbimExtendedVersion",
+                       "format" : "guint16" } ] }
+]
diff --git a/data/mbim-service-ms-basic-connect-extensions-v3.json b/data/mbim-service-ms-basic-connect-extensions-v3.json
new file mode 100644
index 0000000..71ed7aa
--- /dev/null
+++ b/data/mbim-service-ms-basic-connect-extensions-v3.json
@@ -0,0 +1,280 @@
+[
+  // *********************************************************************************
+  { "type" : "Service",
+    "name" : "Ms Basic Connect Extensions V3",
+    "mbimex-service" : "Ms Basic Connect Extensions",
+    "mbimex-version" : "3.0" },
+
+  // *********************************************************************************
+
+  { "name"     : "Device Caps",
+    "type"     : "Command",
+    "since"    : "1.28",
+    // query same as in v1
+    "response" : [ { "name"          : "DeviceType",
+                     "format"        : "guint32",
+                     "public-format" : "MbimDeviceType" },
+                   { "name"          : "CellularClass",
+                     "format"        : "guint32",
+                     "public-format" : "MbimCellularClass" },
+                   { "name"          : "VoiceClass",
+                     "format"        : "guint32",
+                     "public-format" : "MbimVoiceClass" },
+                   { "name"          : "SimClass",
+                     "format"        : "guint32",
+                     "public-format" : "MbimSimClass" },
+                   { "name"          : "DataClass",
+                     "format"        : "guint32",
+                     "public-format" : "MbimDataClassV3" },
+                   { "name"          : "SmsCaps",
+                     "format"        : "guint32",
+                     "public-format" : "MbimSmsCaps" },
+                   { "name"          : "ControlCaps",
+                     "format"        : "guint32",
+                     "public-format" : "MbimCtrlCaps" },
+                   { "name"          : "DataSubclass",
+                     "format"        : "guint64",
+                     "public-format" : "MbimDataSubclass" },
+                   { "name"          : "MaxSessions",
+                     "format"        : "guint32" },
+                   { "name"          : "ExecutorIndex",
+                     "format"        : "guint32" },
+                   { "name"          : "WcdmaBandClass",
+                     "format"        : "guint32" },
+                   { "name"          : "LteBandClass",
+                     "format"        : "tlv-guint16-array" },
+                   { "name"          : "NrBandClass",
+                     "format"        : "tlv-guint16-array" },
+                   { "name"          : "CustomDataClass",
+                     "format"        : "tlv-string" },
+                   { "name"          : "DeviceId",
+                     "format"        : "tlv-string" },
+                   { "name"          : "FirmwareInfo",
+                     "format"        : "tlv-string" },
+                   { "name"          : "HardwareInfo",
+                     "format"        : "tlv-string" } ] },
+
+  //**********************************************************************************
+
+  { "name"         : "Modem Configuration",
+    "type"         : "Command",
+    "since"        : "1.28",
+    "query"        : [],
+    "response"     : [ { "name"          : "ConfigurationStatus",
+			 "format"        : "guint32",
+			 "public-format" : "MbimModemConfigurationStatus" },
+                       { "name"          : "ConfigurationName",
+			 "format"        : "tlv-string" },
+		       { "name"          : "UnnamedIes",
+			 "format"        : "tlv-list" } ],
+    "notification" : [ { "name"          : "ModemConfigurationStatus",
+			 "format"        : "guint32",
+			 "public-format" : "MbimModemConfigurationStatus" },
+                       { "name"          : "ConfigurationName",
+			 "format"        : "tlv-string" },
+		       { "name"          : "UnnamedIes",
+			 "format"        : "tlv-list" } ] },
+
+  //************************************************************************************
+
+  { "name"       : "Registration Parameters",
+    "type"       : "Command",
+    "since"      : "1.28",
+    "set"        : [ { "name"          : "MicoMode",
+                       "format"        : "guint32",
+                       "public-format" : "MbimMicoMode" },
+                     { "name"          : "DrxCycle",
+                       "format"        : "guint32",
+                       "public-format" : "MbimDrxCycle" },
+                     { "name"          : "LadnInfo",
+                       "format"        : "guint32",
+                       "public-format" : "MbimLadnInfo" },
+                     { "name"          : "DefaultPduActivationHint",
+                       "format"        : "guint32",
+                       "public-format" : "MbimDefaultPduActivationHint" },
+                     { "name"          : "ReRegisterIfNedeed",
+                       "format"        : "guint32",
+                       "public-format" : "gboolean" },
+                     { "name"          : "UnnamedIes",
+                       "format"        : "tlv-list" } ],
+    "query"      : [],
+    "response"   : [ { "name"          : "MicoMode",
+                       "format"        : "guint32",
+                       "public-format" : "MbimMicoMode" },
+                     { "name"          : "DrxCycle",
+                       "format"        : "guint32",
+                       "public-format" : "MbimDrxCycle" },
+                     { "name"          : "LadnInfo",
+                       "format"        : "guint32",
+                       "public-format" : "MbimLadnInfo" },
+                     { "name"          : "DefaultPduActivationHint",
+                       "format"        : "guint32",
+                       "public-format" : "MbimDefaultPduActivationHint" },
+                     { "name"          : "ReRegisterIfNedeed",
+                       "format"        : "guint32",
+                       "public-format" : "gboolean" },
+                     { "name"          : "UnnamedIes",
+                       "format"        : "tlv-list" } ] },
+
+  // *********************************************************************************
+  { "name"       : "Lte Attach Info",
+    "type"       : "Command",
+    "since"      : "1.28",
+    // query same as in v1
+    "response"   : [ { "name"          : "LteAttachState",
+                       "format"        : "guint32",
+                       "public-format" : "MbimLteAttachState" },
+                     { "name"          : "NwError",
+                       "format"        : "guint32",
+                       "public-format" : "MbimNwError" },
+                     { "name"          : "IpType",
+                       "format"        : "guint32",
+                       "public-format" : "MbimContextIpType" },
+                     { "name"          : "AccessString",
+                       "format"        : "string" },
+                     { "name"          : "UserName",
+                       "format"        : "string" },
+                     { "name"          : "Password",
+                       "format"        : "string" },
+                     { "name"          : "Compression",
+                       "format"        : "guint32",
+                       "public-format" : "MbimCompression" },
+                     { "name"          : "AuthProtocol",
+                       "format"        : "guint32",
+                       "public-format" : "MbimAuthProtocol" } ],
+  "notification" : [ { "name"          : "LteAttachState",
+                       "format"        : "guint32",
+                       "public-format" : "MbimLteAttachState" },
+                     { "name"          : "NwError",
+                       "format"        : "guint32",
+                       "public-format" : "MbimNwError" },
+                     { "name"          : "IpType",
+                       "format"        : "guint32",
+                       "public-format" : "MbimContextIpType" },
+                     { "name"          : "AccessString",
+                       "format"        : "string" },
+                     { "name"          : "UserName",
+                       "format"        : "string" },
+                     { "name"          : "Password",
+                       "format"        : "string" },
+                     { "name"          : "Compression",
+                       "format"        : "guint32",
+                       "public-format" : "MbimCompression" },
+                     { "name"          : "AuthProtocol",
+                       "format"        : "guint32",
+                       "public-format" : "MbimAuthProtocol" } ] },
+
+  // *********************************************************************************
+  { "name"     : "MbimCellInfoServingNr",
+    "type"     : "Struct",
+    "since"    : "1.28",
+    "contents" : [ { "name"   : "ProviderId",
+                     "format" : "string" },
+                   { "name"   : "Nci",
+                     "format" : "guint64" },
+                   { "name"   : "PhysicalCellID",
+                     "format" : "guint32" },
+                   { "name"   : "Nrarfcn",
+                     "format" : "guint32" },
+                   { "name"   : "Tac",
+                     "format" : "guint32" },
+                   { "name"   : "Rsrp",
+                     "format" : "guint32" },
+                   { "name"   : "Rsrq",
+                     "format" : "guint32" },
+                   { "name"   : "Sinr",
+                     "format" : "guint32" },
+                   { "name"   : "TimingAdvance",
+                     "format" : "guint64" } ] },
+
+  { "name"     : "MbimCellInfoNeighboringNr",
+    "type"     : "Struct",
+    "since"    : "1.28",
+    "contents" : [ { "name"          : "SystemSubType",
+                     "format"        : "guint32",
+                     "public-format" : "MbimDataSubclass" },
+                   { "name"   : "ProviderId",
+                     "format" : "string" },
+                   { "name"   : "CellId",
+                     "format" : "string" },
+                   { "name"   : "PhysicalCellID",
+                     "format" : "guint32" },
+                   { "name"   : "Tac",
+                     "format" : "guint32" },
+                   { "name"   : "Rsrp",
+                     "format" : "guint32" },
+                   { "name"   : "Rsrq",
+                     "format" : "guint32" },
+                   { "name"   : "Sinr",
+                     "format" : "guint32" } ] },
+
+  { "name"       : "Base Stations Info",
+    "type"       : "Command",
+    "since"      : "1.28",
+    "query"      : [ { "name"          : "MaxGsmCount",
+                       "format"        : "guint32" },
+                     { "name"          : "MaxUmtsCount",
+                       "format"        : "guint32" },
+                     { "name"          : "MaxTdscdmaCount",
+                       "format"        : "guint32" },
+                     { "name"          : "MaxLteCount",
+                       "format"        : "guint32" },
+                     { "name"          : "MaxCdmaCount",
+                       "format"        : "guint32" },
+                     { "name"          : "MaxNrCount",
+                       "format"        : "guint32" } ],
+    "response"   : [ { "name"          : "SystemType",
+                       "format"        : "guint32",
+                       "public-format" : "MbimDataClassV3" },
+                     { "name"          : "SystemSubType",
+                       "format"        : "guint32",
+                       "public-format" : "MbimDataSubclass" },
+                     { "name"          : "GsmServingCell",
+                       "format"        : "ms-struct",
+                       "struct-type"   : "MbimCellInfoServingGsm" },
+                     { "name"          : "UmtsServingCell",
+                       "format"        : "ms-struct",
+                       "struct-type"   : "MbimCellInfoServingUmts" },
+                     { "name"          : "TdscdmaServingCell",
+                       "format"        : "ms-struct",
+                       "struct-type"   : "MbimCellInfoServingTdscdma" },
+                     { "name"          : "LteServingCell",
+                       "format"        : "ms-struct",
+                       "struct-type"   : "MbimCellInfoServingLte" },
+                     { "name"          : "GsmNeighboringCells",
+                       "format"        : "ms-struct-array",
+                       "struct-type"   : "MbimCellInfoNeighboringGsm" },
+                     { "name"          : "UmtsNeighboringCells",
+                       "format"        : "ms-struct-array",
+                       "struct-type"   : "MbimCellInfoNeighboringUmts" },
+                     { "name"          : "TdscdmaNeighboringCells",
+                       "format"        : "ms-struct-array",
+                       "struct-type"   : "MbimCellInfoNeighboringTdscdma" },
+                     { "name"          : "LteNeighboringCells",
+                       "format"        : "ms-struct-array",
+                       "struct-type"   : "MbimCellInfoNeighboringLte" },
+                     { "name"          : "CdmaCells",
+                       "format"        : "ms-struct-array",
+                       "struct-type"   : "MbimCellInfoCdma" },
+                     { "name"          : "NrServingCells",
+                       "format"        : "ms-struct-array",
+                       "struct-type"   : "MbimCellInfoServingNr" },
+                     { "name"          : "NrNeighborCells",
+                       "format"        : "ms-struct-array",
+                       "struct-type"   : "MbimCellInfoNeighboringNr" } ] },
+
+  //************************************************************************************
+
+  { "name"      : "Wake Reason",
+    "type"      : "Command",
+    "since"     : "1.28",
+    "query"     : [],
+    "response"  : [  { "name"              : "WakeType",
+                       "format"            : "guint32",
+                       "public-format"     : "MbimWakeType" },
+                     { "name"              : "SessionId",
+                       "format"            : "guint32" },
+                     { "name"              : "WakeTlv",
+                       "format"            : "tlv" } ] }
+
+]
diff --git a/data/mbim-service-ms-basic-connect-extensions.json b/data/mbim-service-ms-basic-connect-extensions.json
index a5097bd..a220ca9 100755
--- a/data/mbim-service-ms-basic-connect-extensions.json
+++ b/data/mbim-service-ms-basic-connect-extensions.json
@@ -19,7 +19,6 @@
                      "array-size-field" : "PcoDataSize" } ] },
 
   { "name"         : "PCO",
-    "service"      : "Ms Basic Connect Extensions",
     "type"         : "Command",
     "since"        : "1.18",
     "query"        : [ { "name"        : "PcoValue",
@@ -60,7 +59,6 @@
                      "public-format" : "MbimAuthProtocol" } ] },
 
   { "name"         : "Lte Attach Configuration",
-    "service"      : "Ms Basic Connect Extensions",
     "type"         : "Command",
     "since"        : "1.18",
     "set"          : [ { "name"          : "Operation",
@@ -89,7 +87,6 @@
   // *********************************************************************************
 
   { "name"         : "Lte Attach Info",
-    "service"      : "Ms Basic Connect Extensions",
     "type"         : "Command",
     "since"        : "1.26",
     "query"        : [],
@@ -133,7 +130,6 @@
   //**********************************************************************************
 
   { "name"     : "Sys Caps",
-    "service"  : "Ms Basic Connect Extensions",
     "type"     : "Command",
     "since"    : "1.26",
     "query"    : [],
@@ -149,7 +145,6 @@
   //**********************************************************************************
 
   { "name"         : "Slot Info Status",
-    "service"      : "Ms Basic Connect Extensions",
     "type"         : "Command",
     "since"        : "1.26",
     "query"        : [ { "name"          : "SlotIndex",
@@ -174,7 +169,6 @@
                      "format" : "guint32" } ] },
 
   { "name"     : "Device Slot Mappings",
-    "service"  : "Ms Basic Connect Extensions",
     "type"     : "Command",
     "since"    : "1.26",
     "set"      : [ { "name"             : "MapCount",
@@ -194,7 +188,6 @@
 //************************************************************************************
 
   { "name"     : "Device Caps",
-    "service"  : "Ms Basic Connect Extensions",
     "type"     : "Command",
     "since"    : "1.26",
     "query"    : [],
@@ -219,8 +212,8 @@
                    { "name"          : "ControlCaps",
                      "format"        : "guint32",
                      "public-format" : "MbimCtrlCaps" },
-                   { "name"   : "MaxSessions",
-                     "format" : "guint32" },
+                   { "name"     : "MaxSessions",
+                     "format"   : "guint32" },
                    { "name"     : "CustomDataClass",
                      "format"   : "string",
                      "max-size" : "22" },
@@ -233,7 +226,337 @@
                    { "name"     : "HardwareInfo",
                      "format"   : "string",
                      "max-size" : "60" },
-                   { "name"   : "ExecutorIndex",
-                     "format" : "guint32" } ] }
+                   { "name"     : "ExecutorIndex",
+                     "format"   : "guint32" } ] },
 
+//************************************************************************************
+
+  { "name"       : "Location Info Status",
+    "type"       : "Command",
+    "since"      : "1.28",
+    "query"      : [],
+    "response"   : [ { "name"   : "LocationAreaCode",
+                       "format" : "guint32" },
+                     { "name"   : "TrackingAreaCode",
+                       "format" : "guint32" },
+                     { "name"   : "CellID",
+                       "format" : "guint32" } ],
+    "notification" : [ { "name"   : "LocationAreaCode",
+                         "format" : "guint32" },
+                       { "name"   : "TrackingAreaCode",
+                         "format" : "guint32" },
+                       { "name"   : "CellID",
+                         "format" : "guint32" } ] },
+
+ // *********************************************************************************
+
+  { "name"     : "MbimProvisionedContextElementV2",
+    "type"     : "Struct",
+    "since"    : "1.28",
+    "contents" : [ { "name"          : "ContextId",
+                     "format"        : "guint32" },
+                   { "name"          : "ContextType",
+                     "format"        : "uuid" },
+                   { "name"          : "IpType",
+                     "format"        : "guint32",
+                     "public-format" : "MbimContextIpType" },
+                   { "name"          : "State",
+                     "format"        : "guint32",
+                     "public-format" : "MbimContextState" },
+                   { "name"          : "Roaming",
+                     "format"        : "guint32",
+                     "public-format" : "MbimContextRoamingControl" },
+                   { "name"          : "MediaType",
+                     "format"        : "guint32",
+                     "public-format" : "MbimContextMediaType" },
+                   { "name"          : "Source",
+                     "format"        : "guint32",
+                     "public-format" : "MbimContextSource" },
+                   { "name"          : "AccessString",
+                     "format"        : "string" },
+                   { "name"          : "UserName",
+                     "format"        : "string" },
+                   { "name"          : "Password",
+                     "format"        : "string" },
+                   { "name"          : "Compression",
+                     "format"        : "guint32",
+                     "public-format" : "MbimCompression" },
+                   { "name"          : "AuthProtocol",
+                     "format"        : "guint32",
+                     "public-format" : "MbimAuthProtocol" } ] },
+
+  { "name"         : "Provisioned Contexts",
+    "type"         : "Command",
+    "since"        : "1.28",
+    "set"          : [ { "name"          : "Operation",
+                         "format"        : "guint32",
+                         "public-format" : "MbimContextOperation" },
+                       { "name"          : "ContextType",
+                         "format"        : "uuid" },
+                       { "name"          : "IpType",
+                         "format"        : "guint32",
+                         "public-format" : "MbimContextIpType" },
+                       { "name"          : "State",
+                         "format"        : "guint32",
+                         "public-format" : "MbimContextState" },
+                       { "name"          : "Roaming",
+                         "format"        : "guint32",
+                         "public-format" : "MbimContextRoamingControl" },
+                       { "name"          : "MediaType",
+                         "format"        : "guint32",
+                         "public-format" : "MbimContextMediaType" },
+                       { "name"          : "Source",
+                         "format"        : "guint32",
+                         "public-format" : "MbimContextSource" },
+                       { "name"          : "AccessString",
+                         "format"        : "string" },
+                       { "name"          : "UserName",
+                         "format"        : "string" },
+                       { "name"          : "Password",
+                         "format"        : "string" },
+                       { "name"          : "Compression",
+                         "format"        : "guint32",
+                         "public-format" : "MbimCompression" },
+                       { "name"          : "AuthProtocol",
+                         "format"        : "guint32",
+                         "public-format" : "MbimAuthProtocol" } ],
+    "query"        : [],
+    "response"     : [ { "name"             : "ProvisionedContextsCount",
+                         "format"           : "guint32" },
+                       { "name"             : "ProvisionedContexts",
+                         "format"           : "ref-struct-array",
+                         "struct-type"      : "MbimProvisionedContextElementV2",
+                         "array-size-field" : "ProvisionedContextsCount" } ],
+    "notification" : [ { "name"             : "ProvisionedContextsCount",
+                         "format"           : "guint32" },
+                       { "name"             : "ProvisionedContexts",
+                         "format"           : "ref-struct-array",
+                         "struct-type"      : "MbimProvisionedContextElementV2",
+                         "array-size-field" : "ProvisionedContextsCount" } ] },
+
+  // *********************************************************************************
+  { "name"     : "MbimCellInfoServingGsm",
+    "type"     : "Struct",
+    "since"    : "1.28",
+    "contents" : [ { "name"   : "ProviderId",
+                     "format" : "string" },
+                   { "name"   : "LocationAreaCode",
+                     "format" : "guint32" },
+                   { "name"   : "CellId",
+                     "format" : "guint32" },
+                   { "name"   : "TimingAdvance",
+                     "format" : "guint32" },
+                   { "name"   : "Arfcn",
+                     "format" : "guint32" },
+                   { "name"   : "BaseStationId",
+                     "format" : "guint32" },
+                   { "name"   : "RxLevel",
+                     "format" : "guint32" } ] },
+
+  { "name"     : "MbimCellInfoServingUmts",
+    "type"     : "Struct",
+    "since"    : "1.28",
+    "contents" : [ { "name"   : "ProviderId",
+                     "format" : "string" },
+                   { "name"   : "LocationAreaCode",
+                     "format" : "guint32" },
+                   { "name"   : "CellId",
+                     "format" : "guint32" },
+                   { "name"   : "FrequencyInfoUL",
+                     "format" : "guint32" },
+                   { "name"   : "FrequencyInfoDL",
+                     "format" : "guint32" },
+                   { "name"   : "FrequencyInfoNT",
+                     "format" : "guint32" },
+                   { "name"   : "Uarfcn",
+                     "format" : "guint32" },
+                   { "name"   : "PrimaryScramblingCode",
+                     "format" : "guint32" },
+                   { "name"   : "Rscp",
+                     "format" : "gint32" },
+                   { "name"   : "Ecno",
+                     "format" : "gint32" },
+                   { "name"   : "PathLoss",
+                     "format" : "guint32" } ] },
+
+  { "name"     : "MbimCellInfoServingTdscdma",
+    "type"     : "Struct",
+    "since"    : "1.28",
+    "contents" : [ { "name"   : "ProviderId",
+                     "format" : "string" },
+                   { "name"   : "LocationAreaCode",
+                     "format" : "guint32" },
+                   { "name"   : "CellId",
+                     "format" : "guint32" },
+                   { "name"   : "Uarfcn",
+                     "format" : "guint32" },
+                   { "name"   : "CellParameterID",
+                     "format" : "guint32" },
+                   { "name"   : "TimingAdvance",
+                     "format" : "guint32" },
+                   { "name"   : "Rscp",
+                     "format" : "gint32" },
+                   { "name"   : "PathLoss",
+                     "format" : "guint32" } ] },
+
+  { "name"     : "MbimCellInfoServingLte",
+    "type"     : "Struct",
+    "since"    : "1.28",
+    "contents" : [ { "name"   : "ProviderId",
+                     "format" : "string" },
+                   { "name"   : "CellId",
+                     "format" : "guint32" },
+                   { "name"   : "Earfcn",
+                     "format" : "guint32" },
+                   { "name"   : "PhysicalCellId",
+                     "format" : "guint32" },
+                   { "name"   : "Tac",
+                     "format" : "guint32" },
+                   { "name"   : "Rsrp",
+                     "format" : "gint32" },
+                   { "name"   : "Rsrq",
+                     "format" : "gint32" },
+                   { "name"   : "TimingAdvance",
+                     "format" : "guint32" } ] },
+
+  { "name"     : "MbimCellInfoNeighboringGsm",
+    "type"     : "Struct",
+    "since"    : "1.28",
+    "contents" : [ { "name"   : "ProviderId",
+                     "format" : "string" },
+                   { "name"   : "LocationAreaCode",
+                     "format" : "guint32" },
+                   { "name"   : "CellId",
+                     "format" : "guint32" },
+                   { "name"   : "Arfcn",
+                     "format" : "guint32" },
+                   { "name"   : "BaseStationId",
+                     "format" : "guint32" },
+                   { "name"   : "RxLevel",
+                     "format" : "guint32" } ] },
+
+
+  { "name"     : "MbimCellInfoNeighboringUmts",
+    "type"     : "Struct",
+    "since"    : "1.28",
+    "contents" : [ { "name"   : "ProviderId",
+                     "format" : "string" },
+                   { "name"   : "LocationAreaCode",
+                     "format" : "guint32" },
+                   { "name"   : "CellId",
+                     "format" : "guint32" },
+                   { "name"   : "Uarfcn",
+                     "format" : "guint32" },
+                   { "name"   : "PrimaryScramblingCode",
+                     "format" : "guint32" },
+                   { "name"   : "Rscp",
+                     "format" : "gint32" },
+                   { "name"   : "Ecno",
+                     "format" : "gint32" },
+                   { "name"   : "PathLoss",
+                     "format" : "guint32" } ] },
+
+  { "name"     : "MbimCellInfoNeighboringTdscdma",
+    "type"     : "Struct",
+    "since"    : "1.28",
+    "contents" : [ { "name"   : "ProviderId",
+                     "format" : "string" },
+                   { "name"   : "LocationAreaCode",
+                     "format" : "guint32" },
+                   { "name"   : "CellId",
+                     "format" : "guint32" },
+                   { "name"   : "Uarfcn",
+                     "format" : "guint32" },
+                   { "name"   : "CellParameterID",
+                     "format" : "guint32" },
+                   { "name"   : "TimingAdvance",
+                     "format" : "guint32" },
+                   { "name"   : "Rscp",
+                     "format" : "gint32" },
+                   { "name"   : "PathLoss",
+                     "format" : "guint32" } ] },
+
+  { "name"     : "MbimCellInfoNeighboringLte",
+    "type"     : "Struct",
+    "since"    : "1.28",
+    "contents" : [ { "name"   : "ProviderId",
+                     "format" : "string" },
+                   { "name"   : "CellId",
+                     "format" : "guint32" },
+                   { "name"   : "Earfcn",
+                     "format" : "guint32" },
+                   { "name"   : "PhysicalCellId",
+                     "format" : "guint32" },
+                   { "name"   : "Tac",
+                     "format" : "guint32" },
+                   { "name"   : "Rsrp",
+                     "format" : "gint32" },
+                   { "name"   : "Rsrq",
+                     "format" : "gint32" } ] },
+
+  { "name"     : "MbimCellInfoCdma",
+    "type"     : "Struct",
+    "since"    : "1.28",
+    "contents" : [ { "name"   : "ServingCellFlag",
+                     "format" : "guint32" },
+                   { "name"   : "Nid",
+                     "format" : "guint32" },
+                   { "name"   : "Sid",
+                     "format" : "guint32" },
+                   { "name"   : "BaseStationId",
+                     "format" : "guint32" },
+                   { "name"   : "BaseLatitude",
+                     "format" : "guint32" },
+                   { "name"   : "BaseLongitude",
+                     "format" : "guint32" },
+                   { "name"   : "RefPn",
+                     "format" : "guint32" },
+                   { "name"   : "GpsSeconds",
+                     "format" : "guint32" },
+                   { "name"   : "PilotStrength",
+                     "format" : "guint32" } ] },
+
+  { "name"     : "Base Stations Info",
+    "type"     : "Command",
+    "since"    : "1.28",
+    "query"    : [ { "name"          : "MaxGsmCount",
+                     "format"        : "guint32" },
+                   { "name"          : "MaxUmtsCount",
+                     "format"        : "guint32" },
+                   { "name"          : "MaxTdscdmaCount",
+                     "format"        : "guint32" },
+                   { "name"          : "MaxLteCount",
+                     "format"        : "guint32" } ,
+                   { "name"          : "MaxCdmaCount",
+                     "format"        : "guint32" } ],
+    "response" : [ { "name"          : "SystemType",
+                     "format"        : "guint32",
+                     "public-format" : "MbimDataClass" },
+                   { "name"          : "GsmServingCell",
+                     "format"        : "ms-struct",
+                     "struct-type"   : "MbimCellInfoServingGsm" },
+                   { "name"          : "UmtsServingCell",
+                     "format"        : "ms-struct",
+                     "struct-type"   : "MbimCellInfoServingUmts" },
+                   { "name"          : "TdscdmaServingCell",
+                     "format"        : "ms-struct",
+                     "struct-type"   : "MbimCellInfoServingTdscdma" },
+                   { "name"          : "LteServingCell",
+                     "format"        : "ms-struct",
+                     "struct-type"   : "MbimCellInfoServingLte" },
+                   { "name"          : "GsmNeighboringCells",
+                     "format"        : "ms-struct-array",
+                     "struct-type"   : "MbimCellInfoNeighboringGsm" },
+                   { "name"          : "UmtsNeighboringCells",
+                     "format"        : "ms-struct-array",
+                     "struct-type"   : "MbimCellInfoNeighboringUmts" },
+                   { "name"          : "TdscdmaNeighboringCells",
+                     "format"        : "ms-struct-array",
+                     "struct-type"   : "MbimCellInfoNeighboringTdscdma" },
+                   { "name"          : "LteNeighboringCells",
+                     "format"        : "ms-struct-array",
+                     "struct-type"   : "MbimCellInfoNeighboringLte" },
+                   { "name"          : "CdmaCells",
+                     "format"        : "ms-struct-array",
+                     "struct-type"   : "MbimCellInfoCdma" } ] }
 ]
diff --git a/data/mbim-service-ms-basic-connect-v2.json b/data/mbim-service-ms-basic-connect-v2.json
new file mode 100644
index 0000000..f8c535a
--- /dev/null
+++ b/data/mbim-service-ms-basic-connect-v2.json
@@ -0,0 +1,155 @@
+
+[
+  // *********************************************************************************
+  { "type"           : "Service",
+    "name"           : "Ms Basic Connect V2",
+    "mbimex-service" : "Basic Connect",
+    "mbimex-version" : "2.0" },
+
+  // *********************************************************************************
+  { "name"         : "Register State",
+    "type"         : "Command",
+    "since"        : "1.28",
+    // set same as in v1
+    // query same as in v1
+    "response"     : [ { "name"          : "NwError",
+                         "format"        : "guint32",
+                         "public-format" : "MbimNwError" },
+                       { "name"          : "RegisterState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimRegisterState" },
+                       { "name"          : "RegisterMode",
+                         "format"        : "guint32",
+                         "public-format" : "MbimRegisterMode" },
+                       { "name"          : "AvailableDataClasses",
+                         "format"        : "guint32",
+                         "public-format" : "MbimDataClass" },
+                       { "name"          : "CurrentCellularClass",
+                         "format"        : "guint32",
+                         "public-format" : "MbimCellularClass" },
+                       { "name"          : "ProviderId",
+                         "format"        : "string" },
+                       { "name"          : "ProviderName",
+                         "format"        : "string" },
+                       { "name"          : "RoamingText",
+                         "format"        : "string" },
+                       { "name"          : "RegistrationFlag",
+                         "format"        : "guint32",
+                         "public-format" : "MbimRegistrationFlag" },
+                       { "name"          : "PreferredDataClasses",
+                         "format"        : "guint32",
+                         "public-format" : "MbimDataClass" } ],
+    "notification" : [ { "name"          : "NwError",
+                         "format"        : "guint32",
+                         "public-format" : "MbimNwError" },
+                       { "name"          : "RegisterState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimRegisterState" },
+                       { "name"          : "RegisterMode",
+                         "format"        : "guint32",
+                         "public-format" : "MbimRegisterMode" },
+                       { "name"          : "AvailableDataClasses",
+                         "format"        : "guint32",
+                         "public-format" : "MbimDataClass" },
+                       { "name"          : "CurrentCellularClass",
+                         "format"        : "guint32",
+                         "public-format" : "MbimCellularClass" },
+                       { "name"          : "ProviderId",
+                         "format"        : "string" },
+                       { "name"          : "ProviderName",
+                         "format"        : "string" },
+                       { "name"          : "RoamingText",
+                         "format"        : "string" },
+                       { "name"          : "RegistrationFlag",
+                         "format"        : "guint32",
+                         "public-format" : "MbimRegistrationFlag" },
+                       { "name"          : "PreferredDataClasses",
+                         "format"        : "guint32",
+                         "public-format" : "MbimDataClass" } ] },
+
+  // *********************************************************************************
+  { "name"         : "Packet Service",
+    "type"         : "Command",
+    "since"        : "1.28",
+    // set same as in v1
+    // query same as in v1
+    "response"     : [ { "name"          : "NwError",
+                         "format"        : "guint32" },
+                       { "name"          : "PacketServiceState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimPacketServiceState" },
+                       { "name"          : "CurrentDataClass",
+                         "format"        : "guint32",
+                         "public-format" : "MbimDataClass" },
+                       { "name"          : "UplinkSpeed",
+                         "format"        : "guint64" },
+                       { "name"          : "DownlinkSpeed",
+                         "format"        : "guint64" },
+                       { "name"          : "FrequencyRange",
+                         "format"        : "guint32",
+                         "public-format" : "MbimFrequencyRange" } ],
+    "notification" : [ { "name"          : "NwError",
+                         "format"        : "guint32" },
+                       { "name"          : "PacketServiceState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimPacketServiceState" },
+                       { "name"          : "CurrentDataClass",
+                         "format"        : "guint32",
+                         "public-format" : "MbimDataClass" },
+                       { "name"          : "UplinkSpeed",
+                         "format"        : "guint64" },
+                       { "name"          : "DownlinkSpeed",
+                         "format"        : "guint64" },
+                       { "name"          : "FrequencyRange",
+                         "format"        : "guint32",
+                         "public-format" : "MbimFrequencyRange" } ] },
+
+  // *********************************************************************************
+  { "name"     : "MbimRsrpSnrInfo",
+    "type"     : "Struct",
+    "since"    : "1.28",
+    "contents" : [ { "name"   : "Rsrp",
+                     "format" : "guint32" },
+                   { "name"   : "Snr",
+                     "format" : "guint32" },
+                   { "name"   : "RsrpThreshold",
+                     "format" : "guint32" },
+                   { "name"   : "SnrThreshold",
+                     "format" : "guint32" },
+                   { "name"          : "SystemType",
+                     "format"        : "guint32",
+                     "public-format" : "MbimDataClass" } ] },
+
+  { "name"         : "Signal State",
+    "type"         : "Command",
+    "since"        : "1.28",
+    // set same as in v1
+    // query same as in v1
+    "response"     : [ { "name"         : "Rssi",
+                         "format"       : "guint32" },
+                       { "name"         : "ErrorRate",
+                         "format"       : "guint32" },
+                       { "name"         : "SignalStrengthInterval",
+                         "format"       : "guint32" },
+                       { "name"         : "RssiThreshold",
+                         "format"       : "guint32" },
+                       { "name"         : "ErrorRateThreshold",
+                         "format"       : "guint32" },
+                       { "name"         : "RsrpSnr",
+                         "format"       : "ms-struct-array" ,
+                         "struct-type"  : "MbimRsrpSnrInfo" } ],
+    "notification" : [ { "name"         : "Rssi",
+                         "format"       : "guint32" },
+                       { "name"         : "ErrorRate",
+                         "format"       : "guint32" },
+                       { "name"         : "SignalStrengthInterval",
+                         "format"       : "guint32" },
+                       { "name"         : "RssiThreshold",
+                         "format"       : "guint32" },
+                       { "name"         : "ErrorRateThreshold",
+                         "format"       : "guint32" },
+                       { "name"         : "RsrpSnr",
+                         "format"       : "ms-struct-array" ,
+                         "struct-type"  : "MbimRsrpSnrInfo" } ] }
+
+]
diff --git a/data/mbim-service-ms-basic-connect-v3.json b/data/mbim-service-ms-basic-connect-v3.json
new file mode 100644
index 0000000..14a7e30
--- /dev/null
+++ b/data/mbim-service-ms-basic-connect-v3.json
@@ -0,0 +1,232 @@
+
+[
+  // *********************************************************************************
+  { "type"           : "Service",
+    "name"           : "Ms Basic Connect V3",
+    "mbimex-service" : "Basic Connect",
+    "mbimex-version" : "3.0" },
+
+ // *********************************************************************************
+  { "name"         : "Subscriber Ready Status",
+    "type"         : "Command",
+    "since"        : "1.28",
+    // query same as in v1
+    "response"     : [ { "name"             : "ReadyState",
+                         "format"           : "guint32",
+                         "public-format"    : "MbimSubscriberReadyState" },
+                       { "name"             : "Flags",
+                         "format"           : "guint32",
+                         "public-format"    : "MbimSubscriberReadyStatusFlag" },
+                       { "name"             : "SubscriberId",
+                         "format"           : "string" },
+                       { "name"             : "SimIccId",
+                         "format"           : "string" },
+                       { "name"             : "ReadyInfo",
+                         "format"           : "guint32",
+                         "public-format"    : "MbimReadyInfoFlag" },
+                       { "name"             : "TelephoneNumbersCount",
+                         "format"           : "guint32" },
+                       { "name"             : "TelephoneNumbers",
+                         "format"           : "string-array",
+                         "array-size-field" : "TelephoneNumbersCount" } ],
+    "notification" : [ { "name"             : "ReadyState",
+                         "format"           : "guint32",
+                         "public-format"    : "MbimSubscriberReadyState" },
+                       { "name"             : "Flags",
+                         "format"           : "guint32",
+                         "public-format"    : "MbimSubscriberReadyStatusFlag" },
+                       { "name"             : "SubscriberId",
+                         "format"           : "string" },
+                       { "name"             : "SimIccId",
+                         "format"           : "string" },
+                       { "name"             : "ReadyInfo",
+                         "format"           : "guint32",
+                         "public-format"    : "MbimReadyInfoFlag" },
+                       { "name"             : "TelephoneNumbersCount",
+                         "format"           : "guint32" },
+                       { "name"             : "TelephoneNumbers",
+                         "format"           : "string-array",
+                         "array-size-field" : "TelephoneNumbersCount" } ] },
+
+  // *********************************************************************************
+  { "name"     : "MbimTai",
+    "type"     : "Struct",
+    "since"    : "1.28",
+    "contents" : [ { "name"   : "PlmnMcc",
+                     "format" : "guint16" },
+                   { "name"   : "PlmnMnc",
+                     "format" : "guint16" },
+                   { "name"   : "Tac",
+                     "format" : "guint32" } ] },
+
+  { "name"         : "Packet Service",
+    "type"         : "Command",
+    "since"    	   : "1.28",
+    // set same as in v1
+    // query same as in v1
+    "response"     : [ { "name"   : "NwError",
+                         "format" : "guint32" },
+                       { "name"          : "PacketServiceState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimPacketServiceState" },
+                       { "name"          : "HighestAvailableDataClass",
+                         "format"        : "guint32",
+                         "public-format" : "MbimDataClassV3" },
+                       { "name"   : "UplinkSpeed",
+                         "format" : "guint64" },
+                       { "name"   : "DownlinkSpeed",
+                         "format" : "guint64" },
+                       { "name"          : "FrequencyRange",
+                         "format"        : "guint32",
+                         "public-format" : "MbimFrequencyRange" },
+                       { "name"          : "DataSubclass",
+                         "format"        : "guint32",
+                         "public-format" : "MbimDataSubclass" },
+                       { "name"        : "Tai",
+                         "format"      : "struct",
+                         "struct-type" : "MbimTai" } ],
+    "notification" : [ { "name"   : "NwError",
+                         "format" : "guint32" },
+                       { "name"          : "PacketServiceState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimPacketServiceState" },
+                       { "name"          : "HighestAvailableDataClass",
+                         "format"        : "guint32",
+                         "public-format" : "MbimDataClassV3" },
+                       { "name"   : "UplinkSpeed",
+                         "format" : "guint64" },
+                       { "name"   : "DownlinkSpeed",
+                         "format" : "guint64" },
+                       { "name"          : "FrequencyRange",
+                         "format"        : "guint32",
+                         "public-format" : "MbimFrequencyRange" },
+                       { "name"          : "DataSubclass",
+                         "format"        : "guint32",
+                         "public-format" : "MbimDataSubclass" },
+                       { "name"        : "Tai",
+                         "format"      : "struct",
+                         "struct-type" : "MbimTai" } ] },
+
+  // *********************************************************************************
+  { "name"         : "Connect",
+    "service"      : "Basic Connect",
+    "type"         : "Command",
+    "since"        : "1.28",
+    "set"          : [ { "name"          : "SessionId",
+                         "format"        : "guint32" },
+                       { "name"          : "ActivationCommand",
+                         "format"        : "guint32",
+                         "public-format" : "MbimActivationCommand" },
+                       { "name"          : "Compression",
+                         "format"        : "guint32",
+                         "public-format" : "MbimCompression" },
+                       { "name"          : "AuthProtocol",
+                         "format"        : "guint32",
+                         "public-format" : "MbimAuthProtocol" },
+                       { "name"          : "IpType",
+                         "format"        : "guint32",
+                         "public-format" : "MbimContextIpType" },
+                       { "name"          : "ContextType",
+                         "format"        : "uuid" },
+                       { "name"          : "MediaPreference",
+                         "format"        : "guint32",
+                         "public-format" : "MbimAccessMediaType" },
+                       { "name"          : "AccessString",
+                         "format"        : "tlv-string" },
+                       { "name"          : "UserName",
+                         "format"        : "tlv-string" },
+                       { "name"          : "Password",
+                         "format"        : "tlv-string" },
+                       { "name"          : "UnnamedIes",
+                         "format"        : "tlv-list" } ],
+    "query"        : [ { "name"   : "SessionId",
+                         "format" : "guint32" } ],
+    "response"     : [ { "name"          : "SessionId",
+                         "format"        : "guint32" },
+                       { "name"          : "ActivationState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimActivationState" },
+                       { "name"          : "VoiceCallState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimVoiceCallState" },
+                       { "name"          : "IpType",
+                         "format"        : "guint32",
+                         "public-format" : "MbimContextIpType" },
+                       { "name"          : "ContextType",
+                         "format"        : "uuid" },
+                       { "name"          : "NwError",
+                         "format"        : "guint32" },
+                       { "name"          : "MediaPreference",
+                         "format"        : "guint32",
+                         "public-format" : "MbimAccessMediaType" },
+                       { "name"          : "AccessString",
+                         "format"        : "tlv-string" },
+                       { "name"          : "UnnamedIes",
+                         "format"        : "tlv-list" } ],
+    "notification" : [ { "name"          : "SessionId",
+                         "format"        : "guint32" },
+                       { "name"          : "ActivationState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimActivationState" },
+                       { "name"          : "VoiceCallState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimVoiceCallState" },
+                       { "name"          : "IpType",
+                         "format"        : "guint32",
+                         "public-format" : "MbimContextIpType" },
+                       { "name"          : "ContextType",
+                         "format"        : "uuid" },
+                       { "name"          : "NwError",
+                         "format"        : "guint32" },
+                       { "name"          : "MediaPreference",
+                         "format"        : "guint32",
+                         "public-format" : "MbimAccessMediaType" },
+                       { "name"          : "AccessString",
+                         "format"        : "tlv-string" },
+                       { "name"          : "UnnamedIes",
+                         "format"        : "tlv-list" } ] },
+
+  // *********************************************************************************
+
+  { "name"     : "MbimPacketFilterV3",
+    "type"     : "Struct",
+    "since"    : "1.28",
+    "contents" : [ { "name"             : "FilterSize",
+                     "format"           : "guint32" },
+                   { "name"             : "PacketFilter",
+                     "format"           : "ref-byte-array",
+                     "array-size-field" : "FilterSize" },
+                   { "name"             : "PacketMask",
+                     "format"           : "ref-byte-array",
+                     "array-size-field" : "FilterSize" },
+                   { "name"             : "FilterId",
+                     "format"           : "guint32" } ] },
+
+  { "name"     : "IP Packet Filters",
+    "type"     : "Command",
+    "since"    : "1.28",
+    "query"    : [ { "name"             : "SessionId",
+                     "format"           : "guint32" },
+                   { "name"             : "PacketFiltersCount",
+                     "format"           : "guint32" },
+                   { "name"             : "PacketFilters",
+                     "format"           : "ref-struct-array",
+                     "struct-type"      : "MbimPacketFilterV3",
+                     "array-size-field" : "PacketFiltersCount" } ],
+    "set"      : [ { "name"             : "SessionId",
+                     "format"           : "guint32" },
+                   { "name"             : "PacketFiltersCount",
+                     "format"           : "guint32" },
+                   { "name"             : "PacketFilters",
+                     "format"           : "ref-struct-array",
+                     "struct-type"      : "MbimPacketFilterV3",
+                     "array-size-field" : "PacketFiltersCount" } ],
+    "response" : [ { "name"             : "SessionId",
+                     "format"           : "guint32" },
+                   { "name"             : "PacketFiltersCount",
+                     "format"           : "guint32" },
+                   { "name"             : "PacketFilters",
+                     "format"           : "ref-struct-array",
+                     "struct-type"      : "MbimPacketFilterV3",
+                     "array-size-field" : "PacketFiltersCount" } ] }
+]
diff --git a/data/mbim-service-ms-firmware-id.json b/data/mbim-service-ms-firmware-id.json
index ea3fafe..bb4a430 100644
--- a/data/mbim-service-ms-firmware-id.json
+++ b/data/mbim-service-ms-firmware-id.json
@@ -5,7 +5,6 @@
 
   // *********************************************************************************
   { "name"     : "Get",
-    "service"  : "MS Firmware ID",
     "type"     : "Command",
     "since"    : "1.8",
     "query"    : [],
diff --git a/data/mbim-service-ms-host-shutdown.json b/data/mbim-service-ms-host-shutdown.json
index 7a3e9b5..2192cc0 100644
--- a/data/mbim-service-ms-host-shutdown.json
+++ b/data/mbim-service-ms-host-shutdown.json
@@ -5,7 +5,6 @@
 
   // *********************************************************************************
   { "name"     : "Notify",
-    "service"  : "MS Host Shutdown",
     "type"     : "Command",
     "since"    : "1.8",
     "set"      : [] }
diff --git a/data/mbim-service-ms-sar.json b/data/mbim-service-ms-sar.json
index 402490d..58b3acc 100644
--- a/data/mbim-service-ms-sar.json
+++ b/data/mbim-service-ms-sar.json
@@ -14,7 +14,6 @@
                      "format" : "guint32" } ] },
 
   { "name"         : "Config",
-    "service"      : "MS SAR",
     "type"         : "Command",
     "since"        : "1.26",
     "set"          : [ { "name"          : "Mode",
@@ -49,7 +48,6 @@
   // *********************************************************************************
 
   { "name"         : "Transmission status",
-    "service"      : "MS SAR",
     "type"         : "Command",
     "since"        : "1.26",
     "set"          : [ { "name"             : "ChannelNotification",
diff --git a/data/mbim-service-ms-uicc-low-level-access.json b/data/mbim-service-ms-uicc-low-level-access.json
index 8307d21..a9db9fe 100644
--- a/data/mbim-service-ms-uicc-low-level-access.json
+++ b/data/mbim-service-ms-uicc-low-level-access.json
@@ -5,7 +5,6 @@
 
   // ************************************************************************
   { "name"     : "ATR",
-    "service"  : "MS UICC Low Level Access",
     "type"     : "Command",
     "since"    : "1.26",
     "query"    : [],
@@ -14,7 +13,6 @@
 
   // ************************************************************************
   { "name"     : "Open Channel",
-    "service"  : "MS UICC Low Level Access",
     "type"     : "Command",
     "since"    : "1.26",
     "set"      : [ { "name"   : "AppId",
@@ -32,7 +30,6 @@
 
   // ************************************************************************
   { "name"     : "Close Channel",
-    "service"  : "MS UICC Low Level Access",
     "type"     : "Command",
     "since"    : "1.26",
     "set"      : [ { "name"   : "Channel",
@@ -44,7 +41,6 @@
 
   // ************************************************************************
   { "name"     : "APDU",
-    "service"  : "MS UICC Low Level Access",
     "type"     : "Command",
     "since"    : "1.26",
     "set"      : [ { "name"   : "Channel",
@@ -71,7 +67,6 @@
                      "format" : "unsized-byte-array" } ] },
 
   { "name"     : "Terminal Capability",
-    "service"  : "MS UICC Low Level Access",
     "type"     : "Command",
     "since"    : "1.26",
     "set"      : [ { "name"   : "TerminalCapabilityCount",
@@ -90,7 +85,6 @@
 
   // *********************************************************************************
   { "name"     : "Reset",
-    "service"  : "MS UICC Low Level Access",
     "type"     : "Command",
     "since"    : "1.26",
     "set"      : [ { "name"          : "PassThroughAction",
diff --git a/data/mbim-service-phonebook.json b/data/mbim-service-phonebook.json
index 452d53f..170b0ee 100644
--- a/data/mbim-service-phonebook.json
+++ b/data/mbim-service-phonebook.json
@@ -6,7 +6,6 @@
 
   // *********************************************************************************
   { "name"         : "Configuration",
-    "service"      : "Phonebook",
     "type"         : "Command",
     "since"        : "1.4",
     "query"        : [],
@@ -45,7 +44,6 @@
                      "format" : "string" } ] },
 
   { "name"     : "Read",
-    "service"  : "Phonebook",
     "type"     : "Command",
     "since"    : "1.4",
     "query"    : [ { "name"          : "FilterFlag",
@@ -62,7 +60,6 @@
 
   // *********************************************************************************
   { "name"     : "Delete",
-    "service"  : "Phonebook",
     "type"     : "Command",
     "since"    : "1.4",
     "set"      : [ { "name"          : "FilterFlag",
@@ -75,7 +72,6 @@
 
   // *********************************************************************************
   { "name"     : "Write",
-    "service"  : "Phonebook",
     "type"     : "Command",
     "since"    : "1.4",
     "set"      : [ { "name"          : "SaveFlag",
diff --git a/data/mbim-service-proxy-control.json b/data/mbim-service-proxy-control.json
index 68e47ef..0450fab 100644
--- a/data/mbim-service-proxy-control.json
+++ b/data/mbim-service-proxy-control.json
@@ -6,13 +6,20 @@
 
   // *********************************************************************************
   { "name"     : "Configuration",
-    "service"  : "Proxy Control",
     "type"     : "Command",
     "since"    : "1.10",
     "set"      : [ { "name"   : "DevicePath",
                      "format" : "string" },
                    { "name"   : "Timeout",
                      "format" : "guint32" } ],
-    "response" : [] }
+    "response" : [] },
 
+  // *********************************************************************************
+  { "name"         : "Version",
+    "type"         : "Command",
+    "since"        : "1.28",
+    "notification" : [ { "name"   : "MbimVersion",
+			 "format" : "guint16" },
+		       { "name"   : "MbimExtendedVersion",
+			 "format" : "guint16" } ] }
 ]
diff --git a/data/mbim-service-qdu.json b/data/mbim-service-qdu.json
index 285f59d..e2ee9da 100644
--- a/data/mbim-service-qdu.json
+++ b/data/mbim-service-qdu.json
@@ -5,7 +5,6 @@
 
   // *********************************************************************************
   { "name"     : "Update Session",
-    "service"  : "QDU",
     "type"     : "Command",
     "since"    : "1.26",
     "set"      : [ { "name"             : "SessionAction",
@@ -31,10 +30,9 @@
                      "format"           : "guint32" },
                    { "name"             : "LastSessionErrorSize",
                      "format"           : "guint32" } ] },
-    
+
   // *********************************************************************************
   { "name"     : "File Open",
-    "service"  : "QDU",
     "type"     : "Command",
     "since"    : "1.26",
     "set"      : [ { "name"             : "FileType",
@@ -47,10 +45,9 @@
                      "format"           : "guint32" },
                    { "name"             : "MaxWindowSize",
                      "format"           : "guint32" } ] },
-    
+
   // *********************************************************************************
   { "name"     : "File Write",
-    "service"  : "QDU",
     "type"     : "Command",
     "since"    : "1.26",
     "set"      : [ { "name"   : "DataBuffer",
diff --git a/data/mbim-service-qmi.json b/data/mbim-service-qmi.json
index eed861e..12ef5bb 100644
--- a/data/mbim-service-qmi.json
+++ b/data/mbim-service-qmi.json
@@ -6,7 +6,6 @@
 
   // *********************************************************************************
   { "name"         : "msg",
-    "service"      : "QMI",
     "type"         : "Command",
     "since-ex"     : { "set"          : "1.14",
                        "response"     : "1.14",
diff --git a/data/mbim-service-sms.json b/data/mbim-service-sms.json
index b6e2ecb..e6c3211 100644
--- a/data/mbim-service-sms.json
+++ b/data/mbim-service-sms.json
@@ -6,7 +6,6 @@
 
   // *********************************************************************************
   { "name"     : "Configuration",
-    "service"  : "SMS",
     "type"     : "Command",
     "since"    : "1.4",
     "set"      : [ { "name"          : "Format",
@@ -57,14 +56,13 @@
                      "public-format" : "MbimSmsCdmaEncoding" },
                    { "name"          : "Language",
                      "format"        : "guint32",
-                     "public-format" : "MbimSmsCdmaLanguage" },
+                     "public-format" : "MbimSmsCdmaLang" },
                    { "name"   : "EncodedMessage",
                      "format" : "ref-byte-array" },
                    { "name"   : "EncodedMessageSizeInCharacters",
                      "format" : "guint32" } ] },
 
   { "name"         : "Read",
-    "service"      : "SMS",
     "type"         : "Command",
     "since"        : "1.4",
     "query"        : [ { "name"          : "Format",
@@ -129,7 +127,7 @@
                      "public-format" : "MbimSmsCdmaEncoding" },
                    { "name"          : "Language",
                      "format"        : "guint32",
-                     "public-format" : "MbimSmsCdmaLanguage" },
+                     "public-format" : "MbimSmsCdmaLang" },
                    { "name"   : "Address",
                      "format" : "string" },
                    { "name"   : "EncodedMessage",
@@ -138,7 +136,6 @@
                      "format" : "guint32" } ] },
 
   { "name"     : "Send",
-    "service"  : "SMS",
     "type"     : "Command",
     "since"    : "1.4",
     "set"      : [ { "name"          : "Format",
@@ -161,7 +158,6 @@
 
   // *********************************************************************************
   { "name"     : "Delete",
-    "service"  : "SMS",
     "type"     : "Command",
     "since"    : "1.4",
     "set"      : [ { "name"          : "Flag",
@@ -173,7 +169,6 @@
 
   // *********************************************************************************
   { "name"         : "Message Store Status",
-    "service"      : "SMS",
     "type"         : "Command",
     "since"        : "1.4",
     "query"        : [],
diff --git a/data/mbim-service-stk.json b/data/mbim-service-stk.json
index 44a7c97..bf3acc7 100644
--- a/data/mbim-service-stk.json
+++ b/data/mbim-service-stk.json
@@ -6,7 +6,6 @@
 
   // *********************************************************************************
   { "name"         : "Pac",
-    "service"      : "STK",
     "type"         : "Command",
     "since"        : "1.4",
     "query"        : [],
@@ -24,7 +23,6 @@
 
   // *********************************************************************************
   { "name"     : "Terminal Response",
-    "service"  : "STK",
     "type"     : "Command",
     "since"    : "1.4",
     "set"      : [ { "name"   : "Response",
@@ -36,7 +34,6 @@
 
   // *********************************************************************************
   { "name"     : "Envelope",
-    "service"  : "STK",
     "type"     : "Command",
     "since"    : "1.4",
     "query"    : [],
diff --git a/data/mbim-service-ussd.json b/data/mbim-service-ussd.json
index 7b0f594..047cdfc 100644
--- a/data/mbim-service-ussd.json
+++ b/data/mbim-service-ussd.json
@@ -6,7 +6,6 @@
 
   // *********************************************************************************
   { "name"         : "",
-    "service"      : "USSD",
     "type"         : "Command",
     "since"        : "1.4",
     "set"          : [ { "name"          : "Action",
diff --git a/docs/man/meson.build b/docs/man/meson.build
index 12c0125..f8daf10 100644
--- a/docs/man/meson.build
+++ b/docs/man/meson.build
@@ -6,17 +6,13 @@
   ['mbim-network', [help2man, '--output=@OUTPUT@', '--name=Simple network management of MBIM devices', mbim_network], []],
 ]
 
-# https://gitlab.freedesktop.org/mobile-broadband/libqmi/-/issues/75 was filed
-# to track the build failure on cross-compilation. This can be re-enabled when
-# the underlying issue is fixed
-#
-# foreach man: mans
-#   custom_target(
-#     man[0],
-#     output: man[0] + '.1',
-#     command: man[1],
-#     depend_files: man[2],
-#     install: true,
-#     install_dir: join_paths(mbim_mandir, 'man1'),
-#   )
-# endforeach
+foreach man: mans
+  custom_target(
+    man[0],
+    output: man[0] + '.1',
+    command: man[1],
+    depend_files: man[2],
+    install: true,
+    install_dir: mbim_mandir / 'man1',
+  )
+endforeach
diff --git a/docs/reference/libmbim-glib/libmbim-glib-common.sections b/docs/reference/libmbim-glib/libmbim-glib-common.sections
index b5961a4..cbcb8fa 100755
--- a/docs/reference/libmbim-glib/libmbim-glib-common.sections
+++ b/docs/reference/libmbim-glib/libmbim-glib-common.sections
@@ -233,6 +233,9 @@
 mbim_device_get_path
 mbim_device_get_path_display
 mbim_device_is_open
+mbim_device_get_ms_mbimex_version
+mbim_device_set_ms_mbimex_version
+mbim_device_check_ms_mbimex_version
 mbim_device_open
 mbim_device_open_finish
 MbimDeviceOpenFlags
@@ -365,6 +368,20 @@
 MbimUiccClassByteType
 MbimUiccPassThroughAction
 MbimUiccPassThroughStatus
+MbimFrequencyRange
+MbimContextRoamingControl
+MbimContextMediaType
+MbimContextState
+MbimContextOperation
+MbimMicoMode
+MbimDefaultPduActivationHint
+MbimLadnInfo
+MbimDrxCycle
+MbimSubscriberReadyStatusFlag
+MbimDataClassV3
+MbimDataSublass
+MbimModemConfigurationStatus
+MbimWakeType
 <SUBSECTION Methods>
 mbim_device_type_get_string
 mbim_cellular_class_build_string_from_mask
@@ -436,6 +453,20 @@
 mbim_uicc_class_byte_type_get_string
 mbim_uicc_pass_through_action_get_string
 mbim_uicc_pass_through_status_get_string
+mbim_frequency_range_build_string_from_mask
+mbim_context_roaming_control_get_string
+mbim_context_media_type_get_string
+mbim_context_state_get_string
+mbim_context_operation_get_string
+mbim_mico_mode_get_string
+mbim_default_pdu_activation_hint_get_string
+mbim_ladn_info_get_string
+mbim_drx_cycle_get_string
+mbim_subscriber_ready_status_flag_build_string_from_mask
+mbim_data_class_v3_build_string_from_mask
+mbim_data_subclass_build_string_from_mask
+mbim_modem_configuration_status_get_string
+mbim_wake_type_get_string
 <SUBSECTION Private>
 mbim_device_type_build_string_from_mask
 mbim_cellular_class_get_string
@@ -509,6 +540,20 @@
 mbim_uicc_class_byte_type_build_string_from_mask
 mbim_uicc_pass_through_action_build_string_from_mask
 mbim_uicc_pass_through_status_build_string_from_mask
+mbim_frequency_range_get_string
+mbim_context_roaming_control_build_string_from_mask
+mbim_context_media_type_build_string_from_mask
+mbim_context_state_build_string_from_mask
+mbim_context_operation_build_string_from_mask
+mbim_mico_mode_build_string_from_mask
+mbim_default_pdu_activation_hint_build_string_from_mask
+mbim_ladn_info_build_string_from_mask
+mbim_drx_cycle_build_string_from_mask
+mbim_subscriber_ready_status_flag_get_string
+mbim_data_class_v3_get_string
+mbim_data_subclass_get_string
+mbim_modem_configuration_status_build_string_from_mask
+mbim_wake_type_build_string_from_mask
 <SUBSECTION Standard>
 MBIM_TYPE_ACTIVATION_COMMAND
 MBIM_TYPE_ACTIVATION_STATE
@@ -585,6 +630,20 @@
 MBIM_TYPE_UICC_CLASS_BYTE_TYPE
 MBIM_TYPE_UICC_PASS_THROUGH_ACTION
 MBIM_TYPE_UICC_PASS_THROUGH_STATUS
+MBIM_TYPE_FREQUENCY_RANGE
+MBIM_TYPE_CONTEXT_ROAMING_CONTROL
+MBIM_TYPE_CONTEXT_MEDIA_TYPE
+MBIM_TYPE_CONTEXT_STATE
+MBIM_TYPE_CONTEXT_OPERATION
+MBIM_TYPE_MICO_MODE
+MBIM_TYPE_DEFAULT_PDU_ACTIVATION_HINT
+MBIM_TYPE_LADN_INFO
+MBIM_TYPE_DRX_CYCLE
+MBIM_TYPE_SUBSCRIBER_READY_STATUS_FLAG
+MBIM_TYPE_DATA_CLASS_V3
+MBIM_TYPE_DATA_SUBCLASS
+MBIM_TYPE_MODEM_CONFIGURATION_STATUS
+MBIM_TYPE_WAKE_TYPE
 mbim_activation_command_get_type
 mbim_activation_state_get_type
 mbim_auth_protocol_get_type
@@ -660,6 +719,20 @@
 mbim_uicc_secure_messaging_get_type
 mbim_uicc_pass_through_action_get_type
 mbim_uicc_pass_through_status_get_type
+mbim_frequency_range_get_type
+mbim_context_roaming_control_get_type
+mbim_context_media_type_get_type
+mbim_context_state_get_type
+mbim_context_operation_get_type
+mbim_mico_mode_get_type
+mbim_default_pdu_activation_hint_get_type
+mbim_ladn_info_get_type
+mbim_drx_cycle_get_type
+mbim_subscriber_ready_status_flag_get_type
+mbim_data_class_v3_get_type
+mbim_data_subclass_get_type
+mbim_modem_configuration_status_get_type
+mbim_wake_type_get_type
 </SECTION>
 
 <SECTION>
@@ -698,6 +771,35 @@
 </SECTION>
 
 <SECTION>
+<FILE>mbim-tlv</FILE>
+MbimTlv
+MbimTlvType
+<SUBSECTION Methods>
+mbim_tlv_new
+mbim_tlv_dup
+mbim_tlv_ref
+mbim_tlv_unref
+mbim_tlv_get_raw
+mbim_tlv_get_tlv_type
+mbim_tlv_get_tlv_data
+mbim_tlv_type_get_string
+<SUBSECTION TlvString>
+mbim_tlv_string_new
+mbim_tlv_string_get
+<SUBSECTION TlvUint16Array>
+mbim_tlv_guint16_array_get
+<SUBSECTION TlvWakeCommand>
+mbim_tlv_wake_command_get
+<SUBSECTION TlvWakePacket>
+mbim_tlv_wake_packet_get
+<SUBSECTION Private>
+mbim_tlv_type_build_string_from_mask
+<SUBSECTION Standard>
+MBIM_TYPE_TLV
+mbim_tlv_get_type
+</SECTION>
+
+<SECTION>
 <FILE>mbim-compat</FILE>
 <SUBSECTION>
 MBIM_CID_BASIC_CONNECT_DEVICE_SERVICE_SUBSCRIBER_LIST
@@ -711,6 +813,10 @@
 mbim_message_ms_basic_connect_extensions_lte_attach_status_query_new
 mbim_message_ms_basic_connect_extensions_lte_attach_status_response_parse
 mbim_message_ms_basic_connect_extensions_lte_attach_status_notification_parse
+<SUBSECTION>
+MBIM_NW_ERROR_UNKNOWN
+<SUBSECTION>
+MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_NETWORK_BLACKLIST
 <SUBSECTION Private>
 MbimDeprecatedCidBasicConnect
 MbimDeprecatedRegistrationFlag
diff --git a/docs/reference/libmbim-glib/libmbim-glib-docs.xml b/docs/reference/libmbim-glib/libmbim-glib-docs.xml
index 92b8226..a9d5d31 100644
--- a/docs/reference/libmbim-glib/libmbim-glib-docs.xml
+++ b/docs/reference/libmbim-glib/libmbim-glib-docs.xml
@@ -59,6 +59,7 @@
     <xi:include href="xml/mbim-enums.xml"/>
     <xi:include href="xml/mbim-errors.xml"/>
     <xi:include href="xml/mbim-utils.xml"/>
+    <xi:include href="xml/mbim-tlv.xml"/>
   </chapter>
 
   <chapter>
@@ -162,6 +163,10 @@
     <title>Index of new symbols in 1.26</title>
     <xi:include href="xml/api-index-1.26.xml"></xi:include>
   </chapter>
+  <chapter id="api-index-1-28" role="1.28">
+    <title>Index of new symbols in 1.28</title>
+    <xi:include href="xml/api-index-1.28.xml"></xi:include>
+  </chapter>
 
   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
 </book>
diff --git a/docs/reference/libmbim-glib/meson.build b/docs/reference/libmbim-glib/meson.build
index 68c39c1..a2795ed 100644
--- a/docs/reference/libmbim-glib/meson.build
+++ b/docs/reference/libmbim-glib/meson.build
@@ -24,9 +24,9 @@
 glib_prefix = glib_dep.get_pkgconfig_variable('prefix')
 
 fixxref_args = [
-  '--html-dir=' + join_paths(mbim_prefix, gnome.gtkdoc_html_dir(doc_module)),
-  '--extra-dir=' + join_paths(glib_prefix, gnome.gtkdoc_html_dir('glib')),
-  '--extra-dir=' + join_paths(glib_prefix, gnome.gtkdoc_html_dir('gio')),
+  '--html-dir=' + (mbim_prefix / gnome.gtkdoc_html_dir(doc_module)),
+  '--extra-dir=' + (glib_prefix / gnome.gtkdoc_html_dir('glib')),
+  '--extra-dir=' + (glib_prefix / gnome.gtkdoc_html_dir('gio')),
 ]
 
 name = doc_module + '-sections.txt'
@@ -63,5 +63,6 @@
   namespace: 'mbim',
   scan_args: scan_args,
   fixxref_args: fixxref_args,
+  content_files: [sections_txt, version_xml],
   install: true,
 )
diff --git a/docs/reference/libmbim-glib/xml/meson.build b/docs/reference/libmbim-glib/xml/meson.build
index 40d4a8d..612f443 100644
--- a/docs/reference/libmbim-glib/xml/meson.build
+++ b/docs/reference/libmbim-glib/xml/meson.build
@@ -1,14 +1,15 @@
 # SPDX-License-Identifier: GPL-2.0-or-later
 # Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
 
-ent_conf = configuration_data()
-ent_conf.set('PACKAGE', meson.project_name())
-ent_conf.set('PACKAGE_BUGREPORT', meson.project_name() + '-devel@lists.freedesktop.org')
-ent_conf.set('PACKAGE_NAME', meson.project_name())
-ent_conf.set('PACKAGE_STRING', '@0@ @1@'.format(meson.project_name(), mbim_version))
-ent_conf.set('PACKAGE_TARNAME', meson.project_name())
-ent_conf.set('PACKAGE_URL', '')
-ent_conf.set('PACKAGE_VERSION', mbim_version)
+ent_conf = {
+  'PACKAGE': mbim_name,
+  'PACKAGE_BUGREPORT': mbim_name + '-devel@lists.freedesktop.org',
+  'PACKAGE_NAME': mbim_name,
+  'PACKAGE_STRING': '@0@ @1@'.format(mbim_name, mbim_version),
+  'PACKAGE_TARNAME': mbim_name,
+  'PACKAGE_URL': '',
+  'PACKAGE_VERSION': mbim_version,
+}
 
 gtkdocentities_ent = configure_file(
   input: 'gtkdocentities.ent.in',
diff --git a/examples/simple-tester-python/simple-tester-python b/examples/simple-tester-python/simple-tester-python
index 5b12dd6..1c40f9d 100755
--- a/examples/simple-tester-python/simple-tester-python
+++ b/examples/simple-tester-python/simple-tester-python
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 #
 # This program is free software; you can redistribute it and/or modify it under
diff --git a/meson.build b/meson.build
index c80ea2f..59f1727 100644
--- a/meson.build
+++ b/meson.build
@@ -3,16 +3,17 @@
 
 project(
   'libmbim', 'c',
-  version: '1.27.0',
+  version: '1.27.2',
   license: ['GPL-2.0-or-later', 'LGPL-2.1-or-later'],
   default_options: [
     'buildtype=debugoptimized',
     'c_std=gnu89',
     'warning_level=2',
   ],
-  meson_version: '>= 0.45.1',
+  meson_version: '>= 0.53.0',
 )
 
+mbim_name = meson.project_name()
 mbim_version = meson.project_version()
 version_array = mbim_version.split('.')
 mbim_major_version = version_array[0].to_int()
@@ -26,8 +27,8 @@
 mbim_libexecdir = get_option('libexecdir')
 mbim_mandir = get_option('mandir')
 
-mbim_glib_include_subdir = meson.project_name() + '-glib'
-mbim_glib_pkgincludedir = join_paths(mbim_includedir, mbim_glib_include_subdir)
+mbim_glib_include_subdir = mbim_name + '-glib'
+mbim_glib_pkgincludedir = mbim_includedir / mbim_glib_include_subdir
 
 # libtool versioning for libmbim-glib (-version-info c:r:a)
 # - If the interface is unchanged, but the implementation has changed or been fixed, then increment r
@@ -44,15 +45,15 @@
 gnome = import('gnome')
 i18n = import('i18n')
 pkg = import('pkgconfig')
-python = import('python3').find_python()
+python = import('python').find_installation('python3')
 
 source_root = meson.current_source_dir()
 
-data_dir = join_paths(source_root, 'data')
-templates_dir = join_paths(source_root, 'build-aux/templates')
+data_dir = source_root / 'data'
+templates_dir = source_root / 'build-aux/templates'
 
-mbim_codegen = find_program(join_paths(source_root, 'build-aux/mbim-codegen/mbim-codegen'))
-mbim_mkenums = find_program(join_paths(source_root, 'build-aux/mbim-mkenums'))
+mbim_codegen = find_program(source_root / 'build-aux/mbim-codegen/mbim-codegen')
+mbim_mkenums = find_program(source_root / 'build-aux/mbim-mkenums')
 
 top_inc = include_directories('.')
 
@@ -149,8 +150,6 @@
   )
 endif
 
-glib_prefix = glib_dep.get_pkgconfig_variable('prefix')
-
 # MBIM username
 mbim_username = get_option('mbim_username')
 enable_mbim_username = (mbim_username != '')
@@ -168,16 +167,14 @@
 config_h.set('MBIM_USERNAME_ENABLED', enable_mbim_username)
 
 # introspection support
-enable_gir = get_option('introspection')
-if enable_gir
-  dependency('gobject-introspection-1.0', version: '>= 0.9.6')
-endif
+enable_gir = dependency('gobject-introspection-1.0', version: '>= 0.9.6', required: get_option('introspection')).found()
 
-version_conf = configuration_data()
-version_conf.set('MBIM_MAJOR_VERSION', mbim_major_version)
-version_conf.set('MBIM_MINOR_VERSION', mbim_minor_version)
-version_conf.set('MBIM_MICRO_VERSION', mbim_micro_version)
-version_conf.set('VERSION', mbim_version)
+version_conf = {
+  'MBIM_MAJOR_VERSION': mbim_major_version,
+  'MBIM_MINOR_VERSION': mbim_minor_version,
+  'MBIM_MICRO_VERSION': mbim_micro_version,
+  'VERSION': mbim_version,
+}
 
 subdir('src')
 subdir('utils')
@@ -187,31 +184,33 @@
   subdir('docs/reference/libmbim-glib')
 endif
 
-help2man = find_program('help2man', required: false)
-if help2man.found()
-  subdir('docs/man')
-endif
+help2man = find_program(
+  'help2man',
+# When meson >= 0.59 is popular enough, replace with
+# required: get_option('man').require(meson.can_run_host_binaries())
+  required: get_option('man'),
+)
+subdir('docs/man', if_found: help2man)
 
 configure_file(
   output: 'config.h',
   configuration: config_h,
 )
 
-meson.add_install_script(
-  'meson_post_install.py',
-  get_option('bindir'),
-)
+summary({
+  'compiler': cc.get_id(),
+  'cflags': cc_flags,
+  'Documentation': enable_gtk_doc,
+  'bash completion': enable_bash_completion,
+  'gobject introspection': enable_gir,
+  'man pages': help2man.found(),
+}, section: 'Build')
 
-output = '\n' + meson.project_name() + ' ' + meson.project_version() + '\n\n'
-output += '  Build\n'
-output += '    compiler:              ' + cc.get_id() + '\n'
-output += '    cflags:                ' + ' '.join(cc_flags) + '\n'
-output += '    Documentation:         ' + enable_gtk_doc.to_string() + '\n'
-output += '    bash completion:       ' + enable_bash_completion.to_string() + '\n'
-output += '    gobject introspection: ' + enable_gir.to_string() + '\n\n'
-output += '  System paths\n'
-output += '    prefix:                ' + mbim_prefix + '\n'
-output += '    udev base directory:   ' + mbim_username + '\n\n'
-output += '  Features\n'
-output += '    MBIM username:         ' + mbim_username
-message(output)
+summary({
+  'prefix': mbim_prefix,
+  'udev base directory': (enable_mbim_username ? udev_udevdir : 'disabled'),
+}, section: 'System paths')
+
+summary({
+  'MBIM username': mbim_username,
+}, section: 'Features')
diff --git a/meson_options.txt b/meson_options.txt
index bc224e5..bfa33ab 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -5,7 +5,8 @@
 
 option('udevdir', type: 'string', value: '', description: 'where udev base directory is')
 
-option('introspection', type: 'boolean', value: true, description: 'build introspection support')
+option('introspection', type: 'feature', value: 'auto', description: 'build introspection support')
 option('gtk_doc', type: 'boolean', value: false, description: 'use gtk-doc to build documentation')
+option('man', type: 'feature', value: 'auto', description: 'build man pages using help2man')
 
 option('bash_completion', type: 'boolean', value: true, description: 'install bash completion files')
diff --git a/meson_post_install.py b/meson_post_install.py
deleted file mode 100755
index b2d5dfd..0000000
--- a/meson_post_install.py
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env python3
-
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
-
-import os
-import subprocess
-import sys
-
-prefix = os.environ['MESON_INSTALL_DESTDIR_PREFIX']
-
-bindir = os.path.join(prefix, sys.argv[1])
-subprocess.check_call(['chmod', '755', os.path.join(bindir, 'mbim-network')])
diff --git a/src/common/meson.build b/src/common/meson.build
index efd1835..055d28c 100644
--- a/src/common/meson.build
+++ b/src/common/meson.build
@@ -9,7 +9,7 @@
 )
 
 libmbim_common_dep = declare_dependency(
-  include_directories: include_directories('.'),
+  include_directories: '.',
   dependencies: glib_dep,
   link_with: libmbim_common,
 )
diff --git a/src/libmbim-glib/generated/meson.build b/src/libmbim-glib/generated/meson.build
index b8ed1c2..deae5f9 100644
--- a/src/libmbim-glib/generated/meson.build
+++ b/src/libmbim-glib/generated/meson.build
@@ -19,7 +19,7 @@
     python,
     mbim_mkenums,
     '--fhead', '#ifndef __LIBMBIM_GLIB_ERROR_TYPES_H__\n#define __LIBMBIM_GLIB_ERROR_TYPES_H__\n#include "mbim-errors.h"\n',
-    '--template', files(join_paths(templates_dir, enum_types + '.h.template')),
+    '--template', files(templates_dir / enum_types + '.h.template'),
     '--ftail', '#endif /* __LIBMBIM_GLIB_ERROR_TYPES_H__ */\n',
     '@INPUT@'],
   capture: true,
@@ -35,7 +35,7 @@
     python,
     mbim_mkenums,
     '--fhead', '#include "mbim-errors.h"\n#include "mbim-error-types.h"\n',
-    '--template', files(join_paths(templates_dir, enum_types + '.c.template')),
+    '--template', files(templates_dir / enum_types + '.c.template'),
     '@INPUT@'],
   capture: true,
 )
@@ -50,7 +50,7 @@
     python,
     mbim_mkenums,
     '--fhead', '#include "mbim-errors.h"\n#include "mbim-error-types.h"\n',
-    '--template', files(join_paths(templates_dir, enum_types + '.c.template')),
+    '--template', files(templates_dir / enum_types + '.c.template'),
     '@INPUT@'],
   capture: true,
 )
@@ -65,8 +65,8 @@
   command: [
     python,
     mbim_mkenums,
-    '--fhead', '#ifndef __LIBMBIM_GLIB_ENUM_TYPES_H__\n#define __LIBMBIM_GLIB_ENUM_TYPES_H__\n#include "mbim-uuid.h"\n#include "mbim-cid.h"\n#include "mbim-message.h"\n#include "mbim-enums.h"\n',
-    '--template', files(join_paths(templates_dir, enum_types + '.h.template')),
+    '--fhead', '#ifndef __LIBMBIM_GLIB_ENUM_TYPES_H__\n#define __LIBMBIM_GLIB_ENUM_TYPES_H__\n#include "mbim-uuid.h"\n#include "mbim-cid.h"\n#include "mbim-message.h"\n#include "mbim-enums.h"\n#include "mbim-tlv.h"\n',
+    '--template', files(templates_dir / enum_types + '.h.template'),
     '--ftail', '#endif /* __LIBMBIM_GLIB_ENUM_TYPES_H__ */\n',
     '@INPUT@'],
   capture: true,
@@ -82,40 +82,45 @@
     python,
     mbim_mkenums,
     '--fhead', '#include "mbim-enum-types.h"\n',
-    '--template', files(join_paths(templates_dir, enum_types + '.c.template')),
+    '--template', files(templates_dir / enum_types + '.c.template'),
     '@INPUT@'],
   capture: true,
 )
 
 services_data = [
-  ['atds', true],
-  ['auth', true],
-  ['basic-connect', true],
-  ['dss', true],
-  ['intel-firmware-update', true],
-  ['ms-basic-connect-extensions', true],
-  ['ms-uicc-low-level-access', true],
-  ['ms-firmware-id', true],
-  ['ms-host-shutdown', true],
-  ['ms-sar', true],
-  ['phonebook', true],
-  ['proxy-control', false],
-  ['qdu', true],
-  ['qmi', true],
-  ['sms', true],
-  ['stk', true],
-  ['ussd', true],
+  [['atds'], true],
+  [['auth'], true],
+  [['basic-connect', 'ms-basic-connect-v2', 'ms-basic-connect-v3'], true],
+  [['dss'], true],
+  [['intel-firmware-update'], true],
+  [['ms-basic-connect-extensions', 'ms-basic-connect-extensions-v2', 'ms-basic-connect-extensions-v3'], true],
+  [['ms-uicc-low-level-access'], true],
+  [['ms-firmware-id'], true],
+  [['ms-host-shutdown'], true],
+  [['ms-sar'], true],
+  [['phonebook'], true],
+  [['proxy-control'], false],
+  [['qdu'], true],
+  [['qmi'], true],
+  [['sms'], true],
+  [['stk'], true],
+  [['ussd'], true],
 ]
 
 foreach service_data: services_data
-  service = service_data[0]
+  service = service_data[0][0]
   name = 'mbim-' + service
 
+  input = []
+  foreach service_file: service_data[0]
+    input += data_dir / 'mbim-service-@0@.json'.format(service_file)
+  endforeach
+
   generated = custom_target(
     name,
-    input: join_paths(data_dir, 'mbim-service-@0@.json'.format(service)),
+    input: input,
     output: [name + '.c', name + '.h', name + '.sections'],
-    command: [mbim_codegen, '--input', '@INPUT@', '--output', join_paths('@OUTDIR@', name)],
+    command: [mbim_codegen, '--output', '@OUTDIR@' / name, '@INPUT@'],
     install: true,
     install_dir: [false, mbim_glib_pkgincludedir, false],
   )
@@ -128,7 +133,7 @@
     #        source file, to the path has to be used. the first workaround is to use the
     #        build paths to point to the files, and the second workaround is to use
     #        custom target objects to force its building.
-    gen_sections += [join_paths(meson.current_build_dir(), name + '.sections')]
+    gen_sections += [meson.current_build_dir() / name + '.sections']
     gen_sections_deps += [generated]
   endif
 endforeach
diff --git a/src/libmbim-glib/libmbim-glib.h b/src/libmbim-glib/libmbim-glib.h
index 7fdf8e0..ca5ac26 100644
--- a/src/libmbim-glib/libmbim-glib.h
+++ b/src/libmbim-glib/libmbim-glib.h
@@ -21,6 +21,7 @@
 #include "mbim-device.h"
 #include "mbim-enums.h"
 #include "mbim-proxy.h"
+#include "mbim-tlv.h"
 
 /* generated */
 #include "mbim-enum-types.h"
diff --git a/src/libmbim-glib/mbim-cid.c b/src/libmbim-glib/mbim-cid.c
index 1b8da5f..46d2a04 100644
--- a/src/libmbim-glib/mbim-cid.c
+++ b/src/libmbim-glib/mbim-cid.c
@@ -120,9 +120,10 @@
 };
 
 /* Note: index of the array is CID-1 */
-#define MBIM_CID_PROXY_CONTROL_LAST MBIM_CID_PROXY_CONTROL_CONFIGURATION
+#define MBIM_CID_PROXY_CONTROL_LAST MBIM_CID_PROXY_CONTROL_VERSION
 static const CidConfig cid_proxy_control_config [MBIM_CID_PROXY_CONTROL_LAST] = {
-    { SET, NO_QUERY, NO_NOTIFY }, /* MBIM_CID_PROXY_CONTROL_CONFIGURATION */
+    { SET,    NO_QUERY, NO_NOTIFY }, /* MBIM_CID_PROXY_CONTROL_CONFIGURATION */
+    { NO_SET, NO_QUERY, NOTIFY    }, /* MBIM_CID_PROXY_CONTROL_VERSION */
 };
 
 /* Note: index of the array is CID-1 */
@@ -148,10 +149,10 @@
 };
 
 /* Note: index of the array is CID-1 */
-#define MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LAST MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_DEVICE_RESET
+#define MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LAST MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_REGISTRATION_PARAMETERS
 static const CidConfig cid_ms_basic_connect_extensions_config [MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LAST] = {
     { SET,    QUERY,    NOTIFY    }, /* MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_PROVISIONED_CONTEXTS */
-    { SET,    QUERY,    NOTIFY    }, /* MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_NETWORK_BLACKLIST */
+    { SET,    QUERY,    NOTIFY    }, /* MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_NETWORK_DENYLIST */
     { SET,    QUERY,    NOTIFY    }, /* MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LTE_ATTACH_CONFIG */
     { SET,    QUERY,    NOTIFY    }, /* MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LTE_ATTACH_STATUS */
     { NO_SET, QUERY,    NO_NOTIFY }, /* MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_SYS_CAPS */
@@ -160,6 +161,13 @@
     { NO_SET, QUERY,    NOTIFY    }, /* MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_SLOT_INFO_STATUS */
     { NO_SET, QUERY,    NOTIFY    }, /* MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_PCO */
     { SET,    NO_QUERY, NO_NOTIFY }, /* MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_DEVICE_RESET */
+    { NO_SET, QUERY,    NO_NOTIFY }, /* MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_BASE_STATIONS_INFO */
+    { NO_SET, QUERY,    NOTIFY    }, /* MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LOCATION_INFO_STATUS */
+    { NO_SET, NO_QUERY, NO_NOTIFY }, /* Unused */
+    { NO_SET, NO_QUERY, NO_NOTIFY }, /* Unused */
+    { NO_SET, QUERY,    NO_NOTIFY }, /* MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_VERSION */
+    { NO_SET, NO_QUERY, NO_NOTIFY }, /* Unused */
+    { NO_SET, NO_QUERY, NO_NOTIFY }, /* MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_REGISTRATION_PARAMETERS */
 };
 
 /* Note: index of the array is CID-1 */
diff --git a/src/libmbim-glib/mbim-cid.h b/src/libmbim-glib/mbim-cid.h
index e9e8d64..13c2700 100644
--- a/src/libmbim-glib/mbim-cid.h
+++ b/src/libmbim-glib/mbim-cid.h
@@ -238,6 +238,7 @@
  * MbimCidProxyControl:
  * @MBIM_CID_PROXY_CONTROL_UNKNOWN: Unknown command.
  * @MBIM_CID_PROXY_CONTROL_CONFIGURATION: Configuration.
+ * @MBIM_CID_PROXY_CONTROL_VERSION: MBIM and MBIMEx Version reporting.
  *
  * MBIM commands in the %MBIM_SERVICE_PROXY_CONTROL service.
  *
@@ -245,7 +246,8 @@
  */
 typedef enum { /*< since=1.10 >*/
     MBIM_CID_PROXY_CONTROL_UNKNOWN       = 0,
-    MBIM_CID_PROXY_CONTROL_CONFIGURATION = 1
+    MBIM_CID_PROXY_CONTROL_CONFIGURATION = 1,
+    MBIM_CID_PROXY_CONTROL_VERSION       = 2,
 } MbimCidProxyControl;
 
 /**
@@ -320,7 +322,7 @@
  * MbimCidMsBasicConnectExtensions:
  * @MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_UNKNOWN: Unknown command.
  * @MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_PROVISIONED_CONTEXTS: Provisioned contexts (v2).
- * @MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_NETWORK_BLACKLIST: Network blacklist.
+ * @MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_NETWORK_DENYLIST: Network denylist. Since 1.28.
  * @MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LTE_ATTACH_CONFIGURATION: LTE attach configuration.
  * @MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LTE_ATTACH_INFO: LTE attach status information. Since 1.26.
  * @MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_SYS_CAPS: System capabilities.
@@ -329,6 +331,12 @@
  * @MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_SLOT_INFO_STATUS: Slot info status.
  * @MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_PCO: Protocol configuration operations (PCO).
  * @MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_DEVICE_RESET: Device reset.
+ * @MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_BASE_STATIONS_INFO: Base stations info. Since 1.28.
+ * @MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LOCATION_INFO_STATUS: Location info status. Since 1.28.
+ * @MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_VERSION: Protocol version query and report, defined in MBIMEx v2.0. Since 1.28.
+ * @MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_MODEM_CONFIGURATION: Modem configuration status, defined in MBIMEx v3.0. Since 1.28.
+ * @MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_REGISTRATION_PARAMETERS: Registration parameters, defined in MBIMEx v3.0. Since 1.28.
+ * @MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_WAKE_REASON: Protocol wake reason query and report, defined in MBIMEx v3.0. Since 1.28
  *
  * MBIM commands in the %MBIM_SERVICE_MS_BASIC_CONNECT_EXTENSIONS service.
  *
@@ -337,7 +345,7 @@
 typedef enum { /*< since=1.18 >*/
     MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_UNKNOWN                  = 0,
     MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_PROVISIONED_CONTEXTS     = 1,
-    MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_NETWORK_BLACKLIST        = 2,
+    MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_NETWORK_DENYLIST         = 2,
     MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LTE_ATTACH_CONFIGURATION = 3,
     MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LTE_ATTACH_INFO          = 4,
     MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_SYS_CAPS                 = 5,
@@ -346,6 +354,12 @@
     MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_SLOT_INFO_STATUS         = 8,
     MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_PCO                      = 9,
     MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_DEVICE_RESET             = 10,
+    MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_BASE_STATIONS_INFO       = 11,
+    MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LOCATION_INFO_STATUS     = 12,
+    MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_VERSION                  = 15,
+    MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_MODEM_CONFIGURATION      = 16,
+    MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_REGISTRATION_PARAMETERS  = 17,
+    MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_WAKE_REASON              = 19,
 } MbimCidMsBasicConnectExtensions;
 
 /* Command helpers */
diff --git a/src/libmbim-glib/mbim-compat.h b/src/libmbim-glib/mbim-compat.h
index a535607..58ac6ae 100644
--- a/src/libmbim-glib/mbim-compat.h
+++ b/src/libmbim-glib/mbim-compat.h
@@ -210,6 +210,36 @@
     MbimLteAttachStatus **out_lte_attach_status,
     GError **error);
 
+/*****************************************************************************/
+/* Network errors fixup */
+
+/* The following type exists just so that we can get deprecation warnings */
+G_DEPRECATED
+typedef int MbimNwErrorDeprecated;
+
+/**
+ * MBIM_NW_ERROR_UNKNOWN:
+ *
+ * Network error not set.
+ *
+ * Since: 1.0
+ * Deprecated: 1.28: Use %MBIM_NW_ERROR_NONE instead.
+ */
+#define MBIM_NW_ERROR_UNKNOWN (MbimNwErrorDeprecated) MBIM_NW_ERROR_NONE
+
+/*****************************************************************************/
+/* Rename blacklist to denylist */
+
+/**
+ * MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_NETWORK_BLACKLIST:
+ *
+ * Network deny list.
+ *
+ * Since: 1.18
+ * Deprecated: 1.28: Use MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_NETWORK_DENYLIST instead.
+ */
+#define MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_NETWORK_BLACKLIST (MbimDeprecatedCidMsBasicConnectExtensions) MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_NETWORK_DENYLIST
+
 #endif /* MBIM_DISABLE_DEPRECATED */
 
 G_END_DECLS
diff --git a/src/libmbim-glib/mbim-device.c b/src/libmbim-glib/mbim-device.c
index 327ce12..133ecb0 100644
--- a/src/libmbim-glib/mbim-device.c
+++ b/src/libmbim-glib/mbim-device.c
@@ -3,8 +3,9 @@
 /*
  * libmbim-glib -- GLib/GIO based library to control MBIM devices
  *
- * Copyright (C) 2013-2014 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2013-2021 Aleksander Morgado <aleksander@aleksander.es>
  * Copyright (C) 2014 Smith Micro Software, Inc.
+ * Copyright (C) 2021 Intel Corporation
  *
  * Implementation based on the 'QmiDevice' GObject from libqmi-glib.
  */
@@ -38,6 +39,8 @@
 #include "mbim-proxy.h"
 #include "mbim-proxy-control.h"
 #include "mbim-net-port-manager.h"
+#include "mbim-basic-connect.h"
+#include "mbim-ms-basic-connect-extensions.h"
 
 static void async_initable_iface_init (GAsyncInitableIface *iface);
 
@@ -111,6 +114,10 @@
     /* message size */
     guint16 max_control_transfer;
 
+    /* MBIM extensions major and minor versions agreed with the device */
+    guint8 ms_mbimex_version_major;
+    guint8 ms_mbimex_version_minor;
+
     /* Link management */
     MbimNetPortManager *net_port_manager;
 };
@@ -434,6 +441,44 @@
     return (self->priv->open_status == OPEN_STATUS_OPEN);
 }
 
+guint8
+mbim_device_get_ms_mbimex_version (MbimDevice *self,
+                                   guint8     *out_ms_mbimex_version_minor)
+{
+    g_return_val_if_fail (MBIM_IS_DEVICE (self), 0);
+
+    if (out_ms_mbimex_version_minor)
+        *out_ms_mbimex_version_minor = self->priv->ms_mbimex_version_minor;
+
+    return self->priv->ms_mbimex_version_major;
+}
+
+gboolean
+mbim_device_set_ms_mbimex_version (MbimDevice *self,
+                                   guint8      ms_mbimex_version_major,
+                                   guint8      ms_mbimex_version_minor,
+                                   GError    **error)
+{
+    g_return_val_if_fail (MBIM_IS_DEVICE (self), FALSE);
+
+    /* no checks that may make this method fail for now */
+    self->priv->ms_mbimex_version_major = ms_mbimex_version_major;
+    self->priv->ms_mbimex_version_minor = ms_mbimex_version_minor;
+    return TRUE;
+}
+
+gboolean
+mbim_device_check_ms_mbimex_version (MbimDevice *self,
+                                     guint8      ms_mbimex_version_major,
+                                     guint8      ms_mbimex_version_minor)
+{
+    g_return_val_if_fail (MBIM_IS_DEVICE (self), FALSE);
+
+    return ((self->priv->ms_mbimex_version_major > ms_mbimex_version_major) ||
+            ((self->priv->ms_mbimex_version_major == ms_mbimex_version_major) &&
+             (self->priv->ms_mbimex_version_minor >= ms_mbimex_version_minor)));
+}
+
 /*****************************************************************************/
 
 static void
@@ -765,6 +810,29 @@
         return;
     }
 
+    /* Indications in the internal proxy control service are not emitted as
+     * signals, they're consumed internally */
+    {
+        guint16 mbim_version;
+        guint16 ms_mbimex_version;
+
+        if ((mbim_message_indicate_status_get_service (indication) == MBIM_SERVICE_PROXY_CONTROL) &&
+            (mbim_message_indicate_status_get_cid (indication) == MBIM_CID_PROXY_CONTROL_VERSION) &&
+            mbim_message_proxy_control_version_notification_parse (indication, &mbim_version, &ms_mbimex_version, NULL)) {
+
+            self->priv->ms_mbimex_version_major = (ms_mbimex_version >> 8) & 0xFF;
+            self->priv->ms_mbimex_version_minor = ms_mbimex_version & 0xFF;
+
+            g_debug ("[%s] version information update reported: version %x.%02x, extended version %x.%02x",
+                     self->priv->path_display,
+                     (mbim_version >> 8) & 0xFF,
+                     mbim_version & 0xFF,
+                     self->priv->ms_mbimex_version_major,
+                     self->priv->ms_mbimex_version_minor);
+            return;
+        }
+    }
+
     g_signal_emit (self, signals[SIGNAL_INDICATE_STATUS], 0, indication);
 }
 
@@ -821,7 +889,12 @@
         if (is_partial_fragment) {
             g_autofree gchar *translated = NULL;
 
-            translated = mbim_message_get_printable (message, ">>>>>> ", TRUE);
+            translated = mbim_message_get_printable_full (message,
+                                                          self->priv->ms_mbimex_version_major,
+                                                          self->priv->ms_mbimex_version_minor,
+                                                          ">>>>>> ",
+                                                          TRUE,
+                                                          NULL);
             g_debug ("[%s] Received message fragment (translated)...\n%s",
                      self->priv->path_display,
                      translated);
@@ -868,7 +941,12 @@
                 g_debug ("[%s] No transaction matched in received message",
                          self->priv->path_display);
                 /* Attempt to print a user friendly dump of the packet anyway */
-                printable = mbim_message_get_printable (message, ">>>>>> ", is_partial_fragment);
+                printable = mbim_message_get_printable_full (message,
+                                                             self->priv->ms_mbimex_version_major,
+                                                             self->priv->ms_mbimex_version_minor,
+                                                             ">>>>>> ",
+                                                             is_partial_fragment,
+                                                             NULL);
                 if (printable)
                     g_debug ("[%s] Received unexpected message (translated)...\n%s",
                              self->priv->path_display,
@@ -913,7 +991,12 @@
             if (mbim_utils_get_traces_enabled ()) {
                 g_autofree gchar *printable = NULL;
 
-                printable = mbim_message_get_printable (ctx->fragments, ">>>>>> ", FALSE);
+                printable = mbim_message_get_printable_full (ctx->fragments,
+                                                             self->priv->ms_mbimex_version_major,
+                                                             self->priv->ms_mbimex_version_minor,
+                                                             ">>>>>> ",
+                                                             FALSE,
+                                                             NULL);
                 g_debug ("[%s] Received message (translated)...\n%s",
                          self->priv->path_display,
                          printable);
@@ -949,7 +1032,12 @@
         if (mbim_utils_get_traces_enabled ()) {
             g_autofree gchar *printable = NULL;
 
-            printable = mbim_message_get_printable (message, ">>>>>> ", FALSE);
+            printable = mbim_message_get_printable_full (message,
+                                                         self->priv->ms_mbimex_version_major,
+                                                         self->priv->ms_mbimex_version_minor,
+                                                         ">>>>>> ",
+                                                         FALSE,
+                                                         NULL);
             g_debug ("[%s] Received message (translated)...\n%s",
                      self->priv->path_display,
                      printable);
@@ -1473,6 +1561,8 @@
     DEVICE_OPEN_CONTEXT_STEP_FLAGS_PROXY,
     DEVICE_OPEN_CONTEXT_STEP_CLOSE_MESSAGE,
     DEVICE_OPEN_CONTEXT_STEP_OPEN_MESSAGE,
+    DEVICE_OPEN_CONTEXT_STEP_DEVICE_SERVICES,
+    DEVICE_OPEN_CONTEXT_STEP_MS_EXT_VERSION,
     DEVICE_OPEN_CONTEXT_STEP_LAST
 } DeviceOpenContextStep;
 
@@ -1491,8 +1581,6 @@
     g_slice_free (DeviceOpenContext, ctx);
 }
 
-static void device_open_context_step (GTask *task);
-
 gboolean
 mbim_device_open_full_finish (MbimDevice    *self,
                               GAsyncResult  *res,
@@ -1509,7 +1597,167 @@
     return mbim_device_open_full_finish (self, res, error);
 }
 
-static void open_message (GTask *task);
+static void device_open_context_step (GTask *task);
+
+static void
+ms_ext_version_message_ready (MbimDevice   *self,
+                              GAsyncResult *res,
+                              GTask        *task)
+{
+    g_autoptr(MbimMessage)  response = NULL;
+    g_autoptr(GError)       error = NULL;
+    guint16                 mbim_version;
+    guint16                 ms_mbimex_version;
+    DeviceOpenContext      *ctx;
+
+    ctx = g_task_get_task_data (task);
+
+    response = mbim_device_command_finish (self, res, &error);
+    if (!response ||
+        !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+        !mbim_message_ms_basic_connect_extensions_v2_version_response_parse (
+            response,
+            &mbim_version,
+            &ms_mbimex_version,
+            &error)){
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    /* We fully ignore the MBIM version for now, we just assume it's 1.0, which
+     * is the only known release from the USB-IF for now. */
+    self->priv->ms_mbimex_version_major = (ms_mbimex_version >> 8) & 0xFF;
+    self->priv->ms_mbimex_version_minor = ms_mbimex_version & 0xFF;
+
+    g_debug ("[%s] successfully exchanged version information: version %x.%02x, extended version %x.%02x",
+             self->priv->path_display,
+             (mbim_version >> 8) & 0xFF,
+             mbim_version & 0xFF,
+             self->priv->ms_mbimex_version_major,
+             self->priv->ms_mbimex_version_minor);
+
+    ctx->step++;
+    device_open_context_step (task);
+}
+
+static void
+ms_ext_version_message (GTask *task)
+{
+    MbimDevice             *self;
+    DeviceOpenContext      *ctx;
+    g_autoptr(MbimMessage)  request = NULL;
+    guint32                 mbim_version = 0;
+    guint32                 ms_mbimex_version = 0;
+
+    self = g_task_get_source_object (task);
+    ctx = g_task_get_task_data (task);
+
+    if ((ctx->flags & MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V2) && (ctx->flags & MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V3)) {
+        g_task_return_new_error (task, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                                 "Cannot request both MBIMEx v2.0 and v3.0 at the same time");
+        g_object_unref (task);
+        return;
+    }
+
+    /* User requested MBIMEx 2.0 or 3.0, so we'll report it along with MBIM 1.0 */
+    mbim_version = 0x01 << 8 | 0x00;
+    if (ctx->flags & MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V2)
+        ms_mbimex_version = 0x02 << 8 | 0x00;
+    else if (ctx->flags & MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V3)
+        ms_mbimex_version = 0x03 << 8 | 0x00;
+    else
+        g_assert_not_reached ();
+
+    request = mbim_message_ms_basic_connect_extensions_v2_version_query_new (mbim_version, ms_mbimex_version, NULL);
+    g_assert (request);
+
+    mbim_device_command (self,
+                         request,
+                         ctx->timeout,
+                         g_task_get_cancellable (task),
+                         (GAsyncReadyCallback)ms_ext_version_message_ready,
+                         task);
+}
+
+static void
+device_services_message_ready (MbimDevice   *device,
+                               GAsyncResult *res,
+                               GTask        *task)
+{
+    g_autoptr(MbimMessage)                    response = NULL;
+    g_autoptr(GError)                         error = NULL;
+    g_autoptr(MbimDeviceServiceElementArray)  device_services = NULL;
+    guint32                                   device_services_count;
+    guint32                                   max_dss_sessions;
+    DeviceOpenContext                        *ctx;
+    guint                                     i;
+
+    ctx = g_task_get_task_data (task);
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response ||
+        !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+        !mbim_message_device_services_response_parse (
+            response,
+            &device_services_count,
+            &max_dss_sessions,
+            &device_services,
+            &error)) {
+        g_task_return_error (task, error);
+        g_object_unref (task);
+        return;
+    }
+
+    if (device_services_count == 0) {
+        g_task_return_new_error (task, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED,
+                                 "No supported services reported by the modem");
+        g_object_unref (task);
+        return;
+    }
+
+    for (i = 0; i < device_services_count; i++) {
+        MbimService service;
+        guint32     j;
+
+        service = mbim_uuid_to_service (&device_services[i]->device_service_id);
+        for (j = 0; j < device_services[i]->cids_count; j++) {
+
+            if ((service == MBIM_SERVICE_MS_BASIC_CONNECT_EXTENSIONS) &&
+                device_services[i]->cids[j] == MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_VERSION) {
+                /* version command is supported, go on */
+                ctx->step++;
+                device_open_context_step (task);
+                return;
+            }
+        }
+    }
+
+    /* the version command isn't supported, so we can just jump to the end */
+    ctx->step = DEVICE_OPEN_CONTEXT_STEP_LAST;;
+    device_open_context_step (task);
+}
+
+static void
+device_services_message (GTask *task)
+{
+    MbimDevice             *self;
+    DeviceOpenContext      *ctx;
+    g_autoptr(MbimMessage)  request = NULL;
+
+    self = g_task_get_source_object (task);
+    ctx  = g_task_get_task_data (task);
+
+    request = mbim_message_device_services_query_new (NULL);
+    g_assert (request);
+
+    mbim_device_command (self,
+                         request,
+                         ctx->timeout,
+                         g_task_get_cancellable (task),
+                         (GAsyncReadyCallback)device_services_message_ready,
+                         task);
+}
 
 static void
 open_message_ready (MbimDevice   *self,
@@ -1773,6 +2021,22 @@
         ctx->step++;
         /* Fall through */
 
+        case DEVICE_OPEN_CONTEXT_STEP_DEVICE_SERVICES:
+        if (ctx->flags & (MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V2 | MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V3)) {
+            device_services_message (task);
+            return;
+        }
+        ctx->step++;
+        /* Fall through */
+
+        case DEVICE_OPEN_CONTEXT_STEP_MS_EXT_VERSION:
+        if (ctx->flags & (MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V2 | MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V3)) {
+            ms_ext_version_message (task);
+            return;
+        }
+        ctx->step++;
+        /* Fall through */
+
     case DEVICE_OPEN_CONTEXT_STEP_LAST:
         /* Nothing else to process, complete without error */
         self->priv->open_status = OPEN_STATUS_OPEN;
@@ -1839,8 +2103,6 @@
 {
     GError *inner_error = NULL;
 
-    self->priv->open_status = OPEN_STATUS_CLOSED;
-
     /* Already closed? */
     if (!self->priv->iochannel && !self->priv->socket_connection && !self->priv->socket_client)
         return TRUE;
@@ -1908,16 +2170,21 @@
                      GAsyncResult *res,
                      GTask        *task)
 {
-    g_autoptr(MbimMessage)  response = NULL;
-    GError                 *error = NULL;
+    g_autoptr(MbimMessage) response = NULL;
+    g_autoptr(GError)      error = NULL;
+    g_autoptr(GError)      iochannel_error = NULL;
 
     response = mbim_device_command_finish (self, res, &error);
-    if (!response)
-        g_task_return_error (task, error);
-    else if (!mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_CLOSE_DONE, &error))
-        g_task_return_error (task, error);
-    else if (!destroy_iochannel (self, &error))
-        g_task_return_error (task, error);
+    if (response)
+        mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_CLOSE_DONE, &error);
+
+    self->priv->open_status = OPEN_STATUS_CLOSED;
+    destroy_iochannel (self, &iochannel_error);
+
+    if (error)
+        g_task_return_error (task, g_steal_pointer (&error));
+    else if (iochannel_error)
+        g_task_return_error (task, g_steal_pointer (&iochannel_error));
     else
         g_task_return_boolean (task, TRUE);
     g_object_unref (task);
@@ -1942,17 +2209,33 @@
     task = g_task_new (self, cancellable, callback, user_data);
     g_task_set_task_data (task, ctx, (GDestroyNotify)device_close_context_free);
 
-    /* Already closed? */
-    if (!self->priv->iochannel) {
+    /* If already closed, we're done */
+    if (self->priv->open_status == OPEN_STATUS_CLOSED) {
         g_task_return_boolean (task, TRUE);
         g_object_unref (task);
         return;
     }
 
+    /* If we're opening, fail with error. We could say we would abort the
+     * ongoing open attempt, but the way to abort that attempt is with the
+     * cancellable given in the open operation, not with an additional close.
+     */
+    if (self->priv->open_status == OPEN_STATUS_OPENING) {
+        g_task_return_new_error (
+            task, MBIM_CORE_ERROR, MBIM_CORE_ERROR_WRONG_STATE,
+            "Cannot close device: not yet fully open");
+        g_object_unref (task);
+        return;
+    }
+
+    g_debug ("[%s] closing device...", self->priv->path_display);
+    g_assert (self->priv->open_status == OPEN_STATUS_OPEN);
+
     /* If the device is in-session, avoid the close message */
     if (self->priv->in_session) {
         GError *error = NULL;
 
+        self->priv->open_status = OPEN_STATUS_CLOSED;
         if (!destroy_iochannel (self, &error))
             g_task_return_error (task, error);
         else
@@ -2076,7 +2359,12 @@
                  ((GByteArray *)message)->len,
                  hex);
 
-        printable = mbim_message_get_printable (message, "<<<<<< ", FALSE);
+        printable = mbim_message_get_printable_full (message,
+                                                     self->priv->ms_mbimex_version_major,
+                                                     self->priv->ms_mbimex_version_minor,
+                                                     "<<<<<< ",
+                                                     FALSE,
+                                                     NULL);
         g_debug ("[%s] Sent message (translated)...\n%s",
                  self->priv->path_display,
                  printable);
@@ -2099,9 +2387,14 @@
         g_byte_array_append (full_fragment, (guint8 *)&fragments[i].header, sizeof (fragments[i].header));
         g_byte_array_append (full_fragment, (guint8 *)&fragments[i].fragment_header, sizeof (fragments[i].fragment_header));
 
-        /* Build dummy message with only headers for printable purposes only */
+        /* Build placeholder message with only headers for printable purposes only */
         if (mbim_utils_get_traces_enabled ())
-            printable_headers = mbim_message_get_printable ((MbimMessage *)full_fragment, "<<<<<< ", TRUE);
+            printable_headers = mbim_message_get_printable_full ((MbimMessage *)full_fragment,
+                                                                 self->priv->ms_mbimex_version_major,
+                                                                 self->priv->ms_mbimex_version_minor,
+                                                                 "<<<<<< ",
+                                                                 TRUE,
+                                                                 NULL);
 
         /* Append the actual fragment data */
         g_byte_array_append (full_fragment, (guint8 *)fragments[i].data, fragments[i].data_length);
@@ -2436,6 +2729,9 @@
     /* Initialize transaction ID */
     self->priv->transaction_id = 0x01;
     self->priv->open_status = OPEN_STATUS_CLOSED;
+
+    /* By default, assume v1.0 supported */
+    self->priv->ms_mbimex_version_major = 0x01;
 }
 
 static void
@@ -2445,6 +2741,7 @@
 
     g_clear_object (&self->priv->file);
 
+    self->priv->open_status = OPEN_STATUS_CLOSED;
     destroy_iochannel (self, NULL);
     g_clear_object (&self->priv->net_port_manager);
 
diff --git a/src/libmbim-glib/mbim-device.h b/src/libmbim-glib/mbim-device.h
index 651b15a..d6b1406 100644
--- a/src/libmbim-glib/mbim-device.h
+++ b/src/libmbim-glib/mbim-device.h
@@ -3,7 +3,8 @@
 /*
  * libmbim-glib -- GLib/GIO based library to control MBIM devices
  *
- * Copyright (C) 2013 - 2014 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2013 - 2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Intel Corporation
  */
 
 #ifndef _LIBMBIM_GLIB_MBIM_DEVICE_H_
@@ -212,17 +213,87 @@
 gboolean mbim_device_is_open (MbimDevice *self);
 
 /**
+ * mbim_device_get_ms_mbimex_version:
+ * @self: a #MbimDevice.
+ * @out_ms_mbimex_version_minor: output location for the minor version number of
+ *  the MS MBIMEx support, or %NULL if not needed.
+ *
+ * Get the version number of the MS MBIMEx support.
+ *
+ * The reported version will be 1 if the initialization sequence to agree on
+ * which version to use hasn't been run (e.g. with mbim_device_open_full() and
+ * the explicit %MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V2 or
+ * %MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V3 flag).
+ *
+ * Returns: the major version number of the MS MBIMEx support.
+ *
+ * Since: 1.28
+ */
+guint8 mbim_device_get_ms_mbimex_version (MbimDevice *self,
+                                          guint8     *out_ms_mbimex_version_minor);
+
+/**
+ * mbim_device_set_ms_mbimex_version:
+ * @self: a #MbimDevice.
+ * @ms_mbimex_version_major: major version number of the MS MBIMEx support.
+ * @ms_mbimex_version_minor: minor version number of the MS MBIMEx support.
+ * @error: Return location for error or %NULL.
+ *
+ * Set the version number of the MS MBIMEx support assumed in the device
+ * instance, which may have been set already by a different process or
+ * device instance.
+ *
+ * If this operation specifies the wrong MBIMEx version agreed between host
+ * and device, the message processing on this device instance may fail.
+ *
+ * This operation does not do any MBIMEx version exchange with the device,
+ * the only way to do that is with mbim_device_open_full() and the explicit
+ * %MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V2 or %MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V3
+ * flag.
+ *
+ * Returns: %TRUE if successful, %FALSE if @error is set.
+ *
+ * Since: 1.28
+ */
+gboolean mbim_device_set_ms_mbimex_version (MbimDevice  *self,
+                                            guint8       ms_mbimex_version_major,
+                                            guint8       ms_mbimex_version_minor,
+                                            GError     **error);
+
+/**
+ * mbim_device_check_ms_mbimex_version:
+ * @self: a #MbimDevice.
+ * @ms_mbimex_version_major: major version number of the MS MBIMEx support.
+ * @ms_mbimex_version_minor: minor version number of the MS MBIMEx support.
+ *
+ * Checks the version number of the MS MBIMEx support in the device instance
+ * against the one given as input.
+ *
+ * Returns: %TRUE if the version of the device instance is the same as or newer
+ * than the passed-in version.
+ *
+ * Since: 1.28
+ */
+gboolean mbim_device_check_ms_mbimex_version (MbimDevice *self,
+                                              guint8      ms_mbimex_version_major,
+                                              guint8      ms_mbimex_version_minor);
+
+/**
  * MbimDeviceOpenFlags:
  * @MBIM_DEVICE_OPEN_FLAGS_NONE: None.
  * @MBIM_DEVICE_OPEN_FLAGS_PROXY: Try to open the port through the 'mbim-proxy'.
+ * @MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V2: Try to enable MS MBIMEx 2.0 support. Since 1.28.
+ * @MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V3: Try to enable MS MBIMEx 3.0 support. Since 1.28.
  *
  * Flags to specify which actions to be performed when the device is open.
  *
  * Since: 1.10
  */
 typedef enum { /*< since=1.10 >*/
-    MBIM_DEVICE_OPEN_FLAGS_NONE  = 0,
-    MBIM_DEVICE_OPEN_FLAGS_PROXY = 1 << 0
+    MBIM_DEVICE_OPEN_FLAGS_NONE         = 0,
+    MBIM_DEVICE_OPEN_FLAGS_PROXY        = 1 << 0,
+    MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V2 = 1 << 1,
+    MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V3 = 1 << 2,
 } MbimDeviceOpenFlags;
 
 /**
diff --git a/src/libmbim-glib/mbim-enums.h b/src/libmbim-glib/mbim-enums.h
old mode 100755
new mode 100644
index a06fb65..11deddb
--- a/src/libmbim-glib/mbim-enums.h
+++ b/src/libmbim-glib/mbim-enums.h
@@ -98,6 +98,8 @@
  * @MBIM_DATA_CLASS_HSDPA: HSDPA.
  * @MBIM_DATA_CLASS_HSUPA: HSUPA.
  * @MBIM_DATA_CLASS_LTE: LTE.
+ * @MBIM_DATA_CLASS_5G_NSA: 5G NSA (MS MBIMEx v2.0). Since 1.28.
+ * @MBIM_DATA_CLASS_5G_SA: 5G SA (MS MBIMEx v2.0). Since 1.28.
  * @MBIM_DATA_CLASS_1XRTT: 1xRTT.
  * @MBIM_DATA_CLASS_1XEVDO: 1xEV-DO.
  * @MBIM_DATA_CLASS_1XEVDO_REVA: 1xEV-DO RevA
@@ -109,6 +111,13 @@
  *
  * Data class.
  *
+ * Both 5G related values are introduced in MBIM Extension v2.0, but given
+ * the update is compatible with the original MBIM enumeration, devices may
+ * report them without having enabled MBIM Extension v2.0 support.
+ *
+ * This type is updated in MBIM Extension v3.0 in a non-backwards compatible
+ * way, see #MbimDataClassV3.
+ *
  * Since: 1.0
  */
 typedef enum { /*< since=1.0 >*/
@@ -118,7 +127,9 @@
     MBIM_DATA_CLASS_HSDPA       = 1 << 3,
     MBIM_DATA_CLASS_HSUPA       = 1 << 4,
     MBIM_DATA_CLASS_LTE         = 1 << 5,
-    /* Bits 6 to 15 reserved for future 3GPP classes */
+    MBIM_DATA_CLASS_5G_NSA      = 1 << 6,
+    MBIM_DATA_CLASS_5G_SA       = 1 << 7,
+    /* Bits 8 to 15 reserved for future 3GPP classes */
     MBIM_DATA_CLASS_1XRTT       = 1 << 16,
     MBIM_DATA_CLASS_1XEVDO      = 1 << 17,
     MBIM_DATA_CLASS_1XEVDO_REVA = 1 << 18,
@@ -150,22 +161,30 @@
 
 /**
  * MbimCtrlCaps:
+ * MBIM_CTRL_CAPS_NONE: None. Since 1.28.
  * @MBIM_CTRL_CAPS_REG_MANUAL: Device allows manual network selection.
  * @MBIM_CTRL_CAPS_HW_RADIO_SWITCH: Device has a hardware radio power switch.
  * @MBIM_CTRL_CAPS_CDMA_MOBILE_IP: The CDMA function supports Mobile IP.
  * @MBIM_CTRL_CAPS_CDMA_SIMPLE_IP: The CDMA function supports Simple IP.
  * @MBIM_CTRL_CAPS_MULTI_CARRIER: Device can work with multiple providers.
+ * @MBIM_CTRL_CAPS_ESIM: Device supports eSIM (MS MBIMEx v3.0). Since 1.28.
+ * @MBIM_CTRL_CAPS_UE_POLICY_ROUTE_SELECTION: Device supports including the route selection descriptors as part of the UE policies (MS MBIMEx v3.0). Since 1.28.
+ * @MBIM_CTRL_CAPS_SIM_HOT_SWAP_CAPABLE: Device supports SIM hot-swap (MS MBIMEx v3.0). Since 1.28.
  *
  * Control capabilities.
  *
  * Since: 1.0
  */
 typedef enum { /*< since=1.0 >*/
-    MBIM_CTRL_CAPS_REG_MANUAL      = 1 << 0,
-    MBIM_CTRL_CAPS_HW_RADIO_SWITCH = 1 << 1,
-    MBIM_CTRL_CAPS_CDMA_MOBILE_IP  = 1 << 2,
-    MBIM_CTRL_CAPS_CDMA_SIMPLE_IP  = 1 << 3,
-    MBIM_CTRL_CAPS_MULTI_CARRIER   = 1 << 4
+    MBIM_CTRL_CAPS_NONE                      = 0,
+    MBIM_CTRL_CAPS_REG_MANUAL                = 1 << 0,
+    MBIM_CTRL_CAPS_HW_RADIO_SWITCH           = 1 << 1,
+    MBIM_CTRL_CAPS_CDMA_MOBILE_IP            = 1 << 2,
+    MBIM_CTRL_CAPS_CDMA_SIMPLE_IP            = 1 << 3,
+    MBIM_CTRL_CAPS_MULTI_CARRIER             = 1 << 4,
+    MBIM_CTRL_CAPS_ESIM                      = 1 << 5,
+    MBIM_CTRL_CAPS_UE_POLICY_ROUTE_SELECTION = 1 << 6,
+    MBIM_CTRL_CAPS_SIM_HOT_SWAP_CAPABLE      = 1 << 7,
 } MbimCtrlCaps;
 
 /*****************************************************************************/
@@ -180,6 +199,8 @@
  * @MBIM_SUBSCRIBER_READY_STATE_FAILURE: Failure.
  * @MBIM_SUBSCRIBER_READY_STATE_NOT_ACTIVATED: Not activated.
  * @MBIM_SUBSCRIBER_READY_STATE_DEVICE_LOCKED: Device locked.
+ * @MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE: The card is ready but does not
+ *  have any enabled profiles (MS MBIMEx). Since 1.28.
  *
  * Ready state of the subscriber.
  *
@@ -193,6 +214,7 @@
     MBIM_SUBSCRIBER_READY_STATE_FAILURE          = 4,
     MBIM_SUBSCRIBER_READY_STATE_NOT_ACTIVATED    = 5,
     MBIM_SUBSCRIBER_READY_STATE_DEVICE_LOCKED    = 6,
+    MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE  = 7,
 } MbimSubscriberReadyState;
 
 /**
@@ -370,6 +392,25 @@
 } MbimProviderState;
 
 /*****************************************************************************/
+/* Connect V3' enums */
+
+/**
+ * MbimAccessMediaType:
+ * @MBIM_ACCESS_MEDIA_TYPE_UNKNOWN: None, or unknown.
+ * @MBIM_ACCESS_MEDIA_TYPE_3GPP: 3GPP only.
+ * @MBIM_ACCESS_MEDIA_TYPE_3GPP_PREFERRED: 3GPP Preferred.
+ *
+ * Access type preference.
+ *
+ * Since: 1.28
+ */
+typedef enum { /*< since=1.28 >*/
+    MBIM_ACCESS_MEDIA_TYPE_UNKNOWN        = 0,
+    MBIM_ACCESS_MEDIA_TYPE_3GPP           = 1,
+    MBIM_ACCESS_MEDIA_TYPE_3GPP_PREFERRED = 2,
+} MbimAccessMediaType;
+
+/*****************************************************************************/
 /* 'Visible Providers' enums */
 
 /**
@@ -391,7 +432,7 @@
 
 /**
  * MbimNwError:
- * @MBIM_NW_ERROR_UNKNOWN: Unknown or unset error.
+ * @MBIM_NW_ERROR_NONE: No error. Since 1.28.
  * @MBIM_NW_ERROR_IMSI_UNKNOWN_IN_HLR: IMSI unknown in the HLR.
  * @MBIM_NW_ERROR_ILLEGAL_MS: Illegal MS. Since 1.10.
  * @MBIM_NW_ERROR_IMSI_UNKNOWN_IN_VLR: IMSI unknown in the VLR.
@@ -443,7 +484,7 @@
  * Since: 1.0
  */
 typedef enum { /*< since=1.0 >*/
-    MBIM_NW_ERROR_UNKNOWN                                                    = 0,
+    MBIM_NW_ERROR_NONE                                                       = 0,
     MBIM_NW_ERROR_IMSI_UNKNOWN_IN_HLR                                        = 2,
     MBIM_NW_ERROR_ILLEGAL_MS                                                 = 3,
     MBIM_NW_ERROR_IMSI_UNKNOWN_IN_VLR                                        = 4,
@@ -562,6 +603,24 @@
 } MbimRegistrationFlag;
 
 /*****************************************************************************/
+/* 'Wake Reason' enums */
+/**
+ * MbimWaketype:
+ * @MBIM_WAKE_TYPE_CID_RESPONSE: A CID response wakes device.
+ * @MBIM_WAKE_TYPE_CID_INDICATION: A CID indication wakes device.
+ * @MBIM_WAKE_TYPE_PACKET: An incoming packet wakes device.
+ *
+ * Wake type.
+ *
+ * Since: 1.28
+ */
+typedef enum { /*< since=1.28 >*/
+    MBIM_WAKE_TYPE_CID_RESPONSE   = 0,
+    MBIM_WAKE_TYPE_CID_INDICATION = 1,
+    MBIM_WAKE_TYPE_PACKET         = 2,
+} MbimWakeType;
+
+/*****************************************************************************/
 /* 'Packet Service' enums */
 
 /**
@@ -1448,6 +1507,285 @@
     MBIM_UICC_PASS_THROUGH_STATUS_ENABLED    = 1,
 } MbimUiccPassThroughStatus;
 
+/*****************************************************************************/
+/* 'Modem Configuration' enums */
+
+/**
+ * MbimModemConfigurationStatus:
+ * @MBIM_MODEM_CONFIGURATION_STATUS_UNKNOWN: Unknown
+ * @MBIM_MODEM_CONFIGURATION_STATUS_STARTED: SIM is ready and configuration started.
+ * @MBIM_MODEM_CONFIGURATION_STATUS_COMPLETED: SIM is ready and configuration completed.
+ *
+ * Modem configuration status.
+ *
+ * Since: 1.28
+ */
+typedef enum {/*< since=1.28 >*/
+    MBIM_MODEM_CONFIGURATION_STATUS_UNKNOWN   = 0,
+    MBIM_MODEM_CONFIGURATION_STATUS_STARTED   = 1,
+    MBIM_MODEM_CONFIGURATION_STATUS_COMPLETED = 2
+} MbimModemConfigurationStatus;
+
+/*****************************************************************************/
+/* 'Packet service v2'  enums */
+
+/**
+ * MbimFrequencyRange:
+ * @MBIM_FREQUENCY_RANGE_UNKNOWN: Unknown.
+ * @MBIM_FREQUENCY_RANGE_1: Frequency range 1 (FR1) in 3GPP TS 38.101-1 (Sub-6G).
+ * @MBIM_FREQUENCY_RANGE_2: FR2 in 3GPP TS 38.101-2 (mmWave).
+ *
+ * Frequency Range.
+ *
+ * Since: 1.28
+ */
+typedef enum { /*< since=1.28 >*/
+    MBIM_FREQUENCY_RANGE_UNKNOWN = 0,
+    MBIM_FREQUENCY_RANGE_1       = 1 << 0,
+    MBIM_FREQUENCY_RANGE_2       = 1 << 1
+} MbimFrequencyRange;
+
+/*****************************************************************************/
+/* 'Provisioned Context v2' enums */
+
+/**
+ * MbimContextRoamingControl:
+ * @MBIM_CONTEXT_ROAMING_CONTROL_HOME_ONLY: Context allowed to be used in home network.
+ * @MBIM_CONTEXT_ROAMING_CONTROL_PARTNER_ONLY: Context allowed to be used in partner network.
+ * @MBIM_CONTEXT_ROAMING_CONTROL_NON_PARTNER_ONLY: Context allowed to be used in non-partner network.
+ * @MBIM_CONTEXT_ROAMING_CONTROL_HOME_AND_PARTNER: Context allowed to be used in home and partner networks.
+ * @MBIM_CONTEXT_ROAMING_CONTROL_HOME_AND_NON_PARTNER: Context allowed to be used in home and non-partner networks.
+ * @MBIM_CONTEXT_ROAMING_CONTROL_PARTNER_AND_NON_PARTNER: Context allowed to be used in partner and non-partner networks.
+ * @MBIM_CONTEXT_ROAMING_CONTROL_ALLOW_ALL: Context allowed to be used in any roaming condition.
+ *
+ * Type of roaming control.
+ *
+ * Since: 1.28
+ */
+typedef enum { /*< since=1.28 >*/
+    MBIM_CONTEXT_ROAMING_CONTROL_HOME_ONLY               = 0,
+    MBIM_CONTEXT_ROAMING_CONTROL_PARTNER_ONLY            = 1,
+    MBIM_CONTEXT_ROAMING_CONTROL_NON_PARTNER_ONLY        = 2,
+    MBIM_CONTEXT_ROAMING_CONTROL_HOME_AND_PARTNER        = 3,
+    MBIM_CONTEXT_ROAMING_CONTROL_HOME_AND_NON_PARTNER    = 4,
+    MBIM_CONTEXT_ROAMING_CONTROL_PARTNER_AND_NON_PARTNER = 5,
+    MBIM_CONTEXT_ROAMING_CONTROL_ALLOW_ALL               = 6
+} MbimContextRoamingControl;
+
+/**
+ * MbimContextMediaType:
+ * @MBIM_CONTEXT_MEDIA_TYPE_CELLULAR_ONLY: Context allowed to be used only wen registered over cellular.
+ * @MBIM_CONTEXT_MEDIA_TYPE_WIFI_ONLY: Context allowed to be used only when registered over Wi-Fi.
+ * @MBIM_CONTEXT_MEDIA_TYPE_ALL: Context allowed to be used when registered either over cellular or Wi-Fi.
+ *
+ * Media type, specifying whether the modem is used for cellular or
+ * iWLAN (Wi-Fi offload).
+ *
+ * Since: 1.28
+ */
+typedef enum { /*< since=1.28 >*/
+    MBIM_CONTEXT_MEDIA_TYPE_CELLULAR_ONLY = 0,
+    MBIM_CONTEXT_MEDIA_TYPE_WIFI_ONLY     = 1,
+    MBIM_CONTEXT_MEDIA_TYPE_ALL           = 2
+} MbimContextMediaType;
+
+/**
+ * MbimContextState:
+ * @MBIM_CONTEXT_STATE_DISABLED: Disabled.
+ * @MBIM_CONTEXT_STATE_ENABLED: Enabled.
+ *
+ * Status of the context, specifying whether it's enabled or disabled.
+ *
+ * Since: 1.28
+ */
+typedef enum { /*< since=1.28 >*/
+    MBIM_CONTEXT_STATE_DISABLED = 0,
+    MBIM_CONTEXT_STATE_ENABLED  = 1,
+} MbimContextState;
+
+/**
+ * MbimContextOperation:
+ * @MBIM_CONTEXT_OPERATION_DEFAULT: Adds or replaces an existing context.
+ * @MBIM_CONTEXT_OPERATION_DELETE: Delete an existing context.
+ * @MBIM_CONTEXT_OPERATION_RESTORE_FACTORY: Restore preconfigured contexts for the provider ID of the current SIM card.
+ *
+ * Command to run when updating context configuration.
+ *
+ * Since: 1.28
+ */
+typedef enum { /*< since=1.28 >*/
+    MBIM_CONTEXT_OPERATION_DEFAULT         = 0,
+    MBIM_CONTEXT_OPERATION_DELETE          = 1,
+    MBIM_CONTEXT_OPERATION_RESTORE_FACTORY = 2,
+} MbimContextOperation;
+
+/*****************************************************************************/
+/* 'Registration Parameters' enums */
+
+/**
+ * MbimMicoMode:
+ * @MBIM_MICO_MODE_DISABLED: Disabled.
+ * @MBIM_MICO_MODE_ENABLED: Enabled.
+ * @MBIM_MICO_MODE_UNSUPPORTED: Device does not support MICO mode.
+ * @MBIM_MICO_MODE_DEFAULT: Default.
+ *
+ * Mobile Initiated Connection Only (MICO) Mode state.
+ *
+ * The @MBIM_MICO_MODE_DEFAULT value should only be used in set operations, it
+ * should never be returned by the module upon a query.
+ *
+ * Since: 1.28
+ */
+typedef enum { /*< since=1.28 >*/
+    MBIM_MICO_MODE_DISABLED    = 0,
+    MBIM_MICO_MODE_ENABLED     = 1,
+    MBIM_MICO_MODE_UNSUPPORTED = 2,
+    MBIM_MICO_MODE_DEFAULT     = 3,
+} MbimMicoMode;
+
+/**
+ * MbimDefaultPduActivationHint:
+ * @MBIM_DEFAULT_PDU_ACTIVATION_HINT_UNLIKELY: Default PDU session activation is unlikely to happen soon.
+ * @MBIM_DEFAULT_PDU_ACTIVATION_HINT_LIKELY: Default PDU session activation is likely to happen soon.
+ *
+ * Hint regarding the activation of the default PDU session.
+ *
+ * Since: 1.28
+ */
+typedef enum { /*< since=1.28 >*/
+    MBIM_DEFAULT_PDU_ACTIVATION_HINT_UNLIKELY = 0,
+    MBIM_DEFAULT_PDU_ACTIVATION_HINT_LIKELY   = 1,
+} MbimDefaultPduActivationHint;
+
+/**
+ * MbimLadnInfo:
+ * @MBIM_LADN_INFO_NOT_NEEDED: Information not needed.
+ * @MBIM_LADN_INFO_REQUESTED: Information requested.
+ *
+ * Local Area Data Network (LADN) information state.
+ *
+ * Since: 1.28
+ */
+typedef enum { /*< since=1.28 >*/
+    MBIM_LADN_INFO_NOT_NEEDED = 0,
+    MBIM_LADN_INFO_REQUESTED  = 1,
+} MbimLadnInfo;
+
+/**
+ * MbimDrxCycle:
+ * @MBIM_DRX_CYCLE_NOT_SPECIFIED: DRX cycle not specified.
+ * @MBIM_DRX_CYCLE_NOT_SUPPORTED: DRX cycle setting not supported.
+ * @MBIM_DRX_CYCLE_32: DRX cycle T=32.
+ * @MBIM_DRX_CYCLE_64: DRX cycle T=64.
+ * @MBIM_DRX_CYCLE_128: DRX cycle T=128.
+ * @MBIM_DRX_CYCLE_256: DRX cycle T=256.
+ *
+ * DRX cycle settings.
+ *
+ * Since: 1.28
+ */
+typedef enum { /*< since=1.28 >*/
+    MBIM_DRX_CYCLE_NOT_SPECIFIED = 0,
+    MBIM_DRX_CYCLE_NOT_SUPPORTED = 1,
+    MBIM_DRX_CYCLE_32            = 2,
+    MBIM_DRX_CYCLE_64            = 3,
+    MBIM_DRX_CYCLE_128           = 4,
+    MBIM_DRX_CYCLE_256           = 5,
+} MbimDrxCycle;
+
+/*****************************************************************************/
+/* 'Subscriber Ready Status v3' enums */
+
+/**
+ * MbimSubscriberReadyStatusFlag:
+ * @MBIM_SUBSCRIBER_READY_STATUS_FLAG_NONE: None.
+ * @MBIM_SUBSCRIBER_READY_STATUS_FLAG_ESIM: Whether the SIM is an eSIM or not.
+ * @MBIM_SUBSCRIBER_READY_STATUS_FLAG_SIM_REMOVABILITY_KNOWN: Whether the SIM removability details are know or not.
+ * @MBIM_SUBSCRIBER_READY_STATUS_FLAG_SIM_REMOVABLE: Whether the SIM is removable or not, valid only if @MBIM_SUBSCRIBER_READY_STATUS_FLAG_SIM_REMOVABLITY_KNOWN is also given.
+ *
+ * Flags specifying SIM related details.
+ *
+ * Since: 1.28
+ */
+typedef enum { /*< since=1.28 >*/
+    MBIM_SUBSCRIBER_READY_STATUS_FLAG_NONE                   = 0,
+    MBIM_SUBSCRIBER_READY_STATUS_FLAG_ESIM                   = 1 << 0,
+    MBIM_SUBSCRIBER_READY_STATUS_FLAG_SIM_REMOVABILITY_KNOWN = 1 << 1,
+    MBIM_SUBSCRIBER_READY_STATUS_FLAG_SIM_REMOVABLE          = 1 << 2,
+} MbimSubscriberReadyStatusFlag;
+
+/*****************************************************************************/
+/* 'Device Caps v3' and 'Base Stations Info v3' enums */
+
+/**
+ * MbimDataClassV3:
+ * @MBIM_DATA_CLASS_V3_GPRS: GPRS.
+ * @MBIM_DATA_CLASS_V3_EDGE: EDGE.
+ * @MBIM_DATA_CLASS_V3_UMTS: UMTS.
+ * @MBIM_DATA_CLASS_V3_HSDPA: HSDPA.
+ * @MBIM_DATA_CLASS_V3_HSUPA: HSUPA.
+ * @MBIM_DATA_CLASS_V3_LTE: LTE.
+ * @MBIM_DATA_CLASS_V3_5G: 5G.
+ * @MBIM_DATA_CLASS_V3_1XRTT: 1xRTT.
+ * @MBIM_DATA_CLASS_V3_1XEVDO: 1xEV-DO.
+ * @MBIM_DATA_CLASS_V3_1XEVDO_REVA: 1xEV-DO RevA
+ * @MBIM_DATA_CLASS_V3_1XEVDV: 1xEV-DV.
+ * @MBIM_DATA_CLASS_V3_3XRTT: 3xRTT.
+ * @MBIM_DATA_CLASS_V3_1XEVDO_REVB: 1xEV-DO RevB.
+ * @MBIM_DATA_CLASS_V3_UMB: UMB.
+ * @MBIM_DATA_CLASS_V3_CUSTOM: Custom.
+ *
+ * Data class update in MBIMEx v3.0.
+ *
+ * There is now a single flag for 5G, and the new #MbimDataSubclass helps to
+ * identify the specific 5G setup.
+ *
+ * This type should be considered incompatible with #MbimDataClass.
+ *
+ * Since: 1.28
+ */
+typedef enum { /*< since=1.28 >*/
+    MBIM_DATA_CLASS_V3_GPRS        = 1 << 0,
+    MBIM_DATA_CLASS_V3_EDGE        = 1 << 1,
+    MBIM_DATA_CLASS_V3_UMTS        = 1 << 2,
+    MBIM_DATA_CLASS_V3_HSDPA       = 1 << 3,
+    MBIM_DATA_CLASS_V3_HSUPA       = 1 << 4,
+    MBIM_DATA_CLASS_V3_LTE         = 1 << 5,
+    MBIM_DATA_CLASS_V3_5G          = 1 << 6,
+    /* Bit 7 unused, bits 8 to 15 reserved for future 3GPP classes */
+    MBIM_DATA_CLASS_V3_1XRTT       = 1 << 16,
+    MBIM_DATA_CLASS_V3_1XEVDO      = 1 << 17,
+    MBIM_DATA_CLASS_V3_1XEVDO_REVA = 1 << 18,
+    MBIM_DATA_CLASS_V3_1XEVDV      = 1 << 19,
+    MBIM_DATA_CLASS_V3_3XRTT       = 1 << 20,
+    MBIM_DATA_CLASS_V3_1XEVDO_REVB = 1 << 21,
+    MBIM_DATA_CLASS_V3_UMB         = 1 << 22,
+    /* Bits 23 to 30 reserved for future 3GPP2 classes */
+    MBIM_DATA_CLASS_V3_CUSTOM      = 1 << 31
+} MbimDataClassV3;
+
+/**
+ * MbimDataSubclass:
+ * @MBIM_DATA_SUBCLASS_NONE: No data subclass.
+ * @MBIM_DATA_SUBCLASS_5G_ENDC: EUTRAN and NR dual connectivity as in 5G option 3.
+ * @MBIM_DATA_SUBCLASS_5G_NR: Standalone NR as in 5G option 2.
+ * @MBIM_DATA_SUBCLASS_5G_NEDC: NR and EUTRAN dual connectivity as in 5G option 4.
+ * @MBIM_DATA_SUBCLASS_5G_ELTE: eLTE as in 5G option 5.
+ * @MBIM_DATA_SUBCLASS_5G_NGENDC: Next-gen eLTE and NR dual connectivity as in 5G option 7.
+ *
+ * Flags specifying the data subclass.
+ *
+ * Since: 1.28
+ */
+typedef enum { /*< since=1.28 >*/
+    MBIM_DATA_SUBCLASS_NONE      = 0,
+    MBIM_DATA_SUBCLASS_5G_ENDC   = 1 << 0,
+    MBIM_DATA_SUBCLASS_5G_NR     = 1 << 1,
+    MBIM_DATA_SUBCLASS_5G_NEDC   = 1 << 2,
+    MBIM_DATA_SUBCLASS_5G_ELTE   = 1 << 3,
+    MBIM_DATA_SUBCLASS_5G_NGENDC = 1 << 4,
+} MbimDataSubclass;
+
 G_END_DECLS
 
 #endif /* _LIBMBIM_GLIB_MBIM_ENUMS_H_ */
diff --git a/src/libmbim-glib/mbim-message-private.h b/src/libmbim-glib/mbim-message-private.h
index c0638ed..99e878c 100644
--- a/src/libmbim-glib/mbim-message-private.h
+++ b/src/libmbim-glib/mbim-message-private.h
@@ -18,6 +18,7 @@
 #include <glib.h>
 
 #include "mbim-message.h"
+#include "mbim-tlv.h"
 
 G_BEGIN_DECLS
 
@@ -168,8 +169,12 @@
                                                               gboolean           swapped_offset_length);
 void               _mbim_struct_builder_append_uuid          (MbimStructBuilder *builder,
                                                               const MbimUuid    *value);
+void               _mbim_struct_builder_append_guint16       (MbimStructBuilder *builder,
+                                                              guint16            value);
 void               _mbim_struct_builder_append_guint32       (MbimStructBuilder *builder,
                                                               guint32            value);
+void               _mbim_struct_builder_append_gint32        (MbimStructBuilder *builder,
+                                                              gint32             value);
 void               _mbim_struct_builder_append_guint32_array (MbimStructBuilder *builder,
                                                               const guint32     *values,
                                                               guint32            n_values);
@@ -192,6 +197,8 @@
 void               _mbim_struct_builder_append_ipv6_array    (MbimStructBuilder *builder,
                                                               const MbimIPv6    *values,
                                                               guint32            n_values);
+void               _mbim_struct_builder_append_string_tlv    (MbimStructBuilder *builder,
+                                                              const gchar       *values);
 
 /*****************************************************************************/
 /* Message builder */
@@ -215,6 +222,8 @@
                                                                                gboolean                   swapped_offset_length);
 void                       _mbim_message_command_builder_append_uuid          (MbimMessageCommandBuilder *builder,
                                                                                const MbimUuid            *value);
+void                       _mbim_message_command_builder_append_guint16       (MbimMessageCommandBuilder *builder,
+                                                                               guint16                    value);
 void                       _mbim_message_command_builder_append_guint32       (MbimMessageCommandBuilder *builder,
                                                                                guint32                    value);
 void                       _mbim_message_command_builder_append_guint32_array (MbimMessageCommandBuilder *builder,
@@ -239,6 +248,12 @@
 void                       _mbim_message_command_builder_append_ipv6_array    (MbimMessageCommandBuilder *builder,
                                                                                const MbimIPv6            *values,
                                                                                guint32                    n_values);
+void                       _mbim_message_command_builder_append_tlv           (MbimMessageCommandBuilder *builder,
+                                                                               const MbimTlv             *tlv);
+void                       _mbim_message_command_builder_append_tlv_string    (MbimMessageCommandBuilder *builder,
+                                                                               const gchar               *str);
+void                       _mbim_message_command_builder_append_tlv_list      (MbimMessageCommandBuilder *builder,
+                                                                               const GList               *tlvs);
 
 /*****************************************************************************/
 /* Message parser */
@@ -257,10 +272,18 @@
                                            guint32             relative_offset,
                                            const MbimUuid    **uuid,
                                            GError            **error);
+gboolean _mbim_message_read_guint16      (const MbimMessage  *self,
+                                           guint32             relative_offset,
+                                           guint16            *value,
+                                           GError            **error);
 gboolean _mbim_message_read_guint32       (const MbimMessage  *self,
                                            guint32             relative_offset,
                                            guint32            *value,
                                            GError            **error);
+gboolean _mbim_message_read_gint32        (const MbimMessage  *self,
+                                           guint32             relative_offset,
+                                           gint32             *value,
+                                           GError            **error);
 gboolean _mbim_message_read_guint32_array (const MbimMessage  *self,
                                            guint32             array_size,
                                            guint32             relative_offset_array_start,
@@ -302,6 +325,28 @@
                                            MbimIPv6          **array,
                                            GError            **error);
 
+gboolean _mbim_message_read_tlv               (const MbimMessage  *self,
+                                               guint32             relative_offset,
+                                               MbimTlv           **tlv,
+                                               guint32            *bytes_read,
+                                               GError            **error);
+gboolean _mbim_message_read_tlv_string        (const MbimMessage  *self,
+                                               guint32             relative_offset,
+                                               gchar             **str,
+                                               guint32            *bytes_read,
+                                               GError            **error);
+gboolean _mbim_message_read_tlv_guint16_array (const MbimMessage  *self,
+                                               guint32             relative_offset,
+                                               guint32            *array_size,
+                                               guint16           **array,
+                                               guint32            *bytes_read,
+                                               GError            **error);
+gboolean _mbim_message_read_tlv_list          (const MbimMessage  *self,
+                                               guint32             relative_offset,
+                                               GList             **tlv,
+                                               guint32            *bytes_read,
+                                               GError            **error);
+
 G_END_DECLS
 
 #endif /* _LIBMBIM_GLIB_MBIM_MESSAGE_PRIVATE_H_ */
diff --git a/src/libmbim-glib/mbim-message.c b/src/libmbim-glib/mbim-message.c
index 2a8f092..7b6699d 100644
--- a/src/libmbim-glib/mbim-message.c
+++ b/src/libmbim-glib/mbim-message.c
@@ -16,6 +16,7 @@
 #include "mbim-message-private.h"
 #include "mbim-error-types.h"
 #include "mbim-enum-types.h"
+#include "mbim-tlv-private.h"
 
 #include "mbim-basic-connect.h"
 #include "mbim-auth.h"
@@ -151,6 +152,34 @@
 }
 
 gboolean
+_mbim_message_read_guint16 (const MbimMessage  *self,
+                            guint32             relative_offset,
+                            guint16            *value,
+                            GError            **error)
+{
+    guint64 required_size;
+    guint32 information_buffer_offset;
+
+    g_assert (value);
+
+    information_buffer_offset = _mbim_message_get_information_buffer_offset (self);
+
+    required_size = (guint64)information_buffer_offset + (guint64)relative_offset + 2;
+    if ((guint64)self->len < required_size) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_MESSAGE,
+                     "cannot read 16bit unsigned integer (2 bytes) (%u < %" G_GUINT64_FORMAT ")",
+                     self->len, required_size);
+        return FALSE;
+    }
+
+    *value = GUINT16_FROM_LE (G_STRUCT_MEMBER (
+                                  guint16,
+                                  self->data,
+                                  (information_buffer_offset + relative_offset)));
+    return TRUE;
+}
+
+gboolean
 _mbim_message_read_guint32 (const MbimMessage  *self,
                             guint32             relative_offset,
                             guint32            *value,
@@ -179,6 +208,34 @@
 }
 
 gboolean
+_mbim_message_read_gint32 (const MbimMessage  *self,
+                           guint32             relative_offset,
+                           gint32             *value,
+                           GError            **error)
+{
+    guint64 required_size;
+    guint32 information_buffer_offset;
+
+    g_assert (value);
+
+    information_buffer_offset = _mbim_message_get_information_buffer_offset (self);
+
+    required_size = (guint64)information_buffer_offset + (guint64)relative_offset + 4;
+    if ((guint64)self->len < required_size) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_MESSAGE,
+                     "cannot read 32bit signed integer (4 bytes) (%u < %" G_GUINT64_FORMAT ")",
+                     self->len, required_size);
+        return FALSE;
+    }
+
+    *value = GINT32_FROM_LE (G_STRUCT_MEMBER (
+                                 gint32,
+                                 self->data,
+                                 (information_buffer_offset + relative_offset)));
+    return TRUE;
+}
+
+gboolean
 _mbim_message_read_guint32_array (const MbimMessage  *self,
                                   guint32             array_size,
                                   guint32             relative_offset_array_start,
@@ -757,6 +814,151 @@
     return TRUE;
 }
 
+gboolean
+_mbim_message_read_tlv (const MbimMessage  *self,
+                        guint32             relative_offset,
+                        MbimTlv           **tlv,
+                        guint32            *bytes_read,
+                        GError            **error)
+{
+    guint32       information_buffer_offset;
+    guint64       tlv_offset;
+    guint64       required_size;
+    const guint8 *tlv_raw;
+    guint64       tlv_size;
+
+    information_buffer_offset = _mbim_message_get_information_buffer_offset (self);
+    tlv_offset = (guint64)information_buffer_offset + (guint64)relative_offset;
+    tlv_raw = (const guint8 *) G_STRUCT_MEMBER_P (self->data, tlv_offset);
+    tlv_size = ((guint64)sizeof (struct tlv) +
+                (guint64)GUINT32_FROM_LE (((struct tlv *)tlv_raw)->data_length) +
+                (guint64)((struct tlv *)tlv_raw)->padding_length);
+
+    required_size = tlv_offset + tlv_size;
+    if ((guint64)self->len < required_size) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_MESSAGE,
+                     "cannot read TLV (%" G_GUINT64_FORMAT " bytes) (%u < %" G_GUINT64_FORMAT ")",
+                     tlv_size, self->len, required_size);
+        return FALSE;
+    }
+
+    *tlv = _mbim_tlv_new_from_raw (tlv_raw, (guint32)tlv_size, bytes_read, error);
+    return (*tlv) ? TRUE : FALSE;
+}
+
+gboolean
+_mbim_message_read_tlv_string (const MbimMessage  *self,
+                               guint32             relative_offset,
+                               gchar             **str,
+                               guint32            *bytes_read,
+                               GError            **error)
+{
+    g_autoptr(MbimTlv)  tlv = NULL;
+    guint32             tlv_bytes_read = 0;
+    gchar              *tlv_str;
+
+    if (!_mbim_message_read_tlv (self,
+                                 relative_offset,
+                                 &tlv,
+                                 &tlv_bytes_read,
+                                 error))
+        return FALSE;
+
+    tlv_str = mbim_tlv_string_get (tlv, error);
+    if (!tlv_str)
+        return FALSE;
+
+    *str = tlv_str;
+    *bytes_read = tlv_bytes_read;
+    return TRUE;
+}
+
+gboolean
+_mbim_message_read_tlv_guint16_array (const MbimMessage  *self,
+                                      guint32             relative_offset,
+                                      guint32            *array_size,
+                                      guint16           **array,
+                                      guint32            *bytes_read,
+                                      GError            **error)
+{
+    g_autoptr(MbimTlv) tlv = NULL;
+    guint32            tlv_bytes_read = 0;
+
+    if (!_mbim_message_read_tlv (self,
+                                 relative_offset,
+                                 &tlv,
+                                 &tlv_bytes_read,
+                                 error))
+        return FALSE;
+
+    if (!mbim_tlv_guint16_array_get (tlv, array_size, array, error))
+        return FALSE;
+
+    *bytes_read = tlv_bytes_read;
+    return TRUE;
+}
+
+gboolean
+_mbim_message_read_tlv_list (const MbimMessage  *self,
+                             guint32             relative_offset,
+                             GList             **tlv_list,
+                             guint32            *bytes_read,
+                             GError            **error)
+{
+    guint32       information_buffer_offset;
+    guint64       tlv_list_offset;
+    const guint8 *tlv_list_raw;
+    guint32       tlv_list_raw_size;
+    GList        *list = NULL;
+    guint32       total_bytes_read = 0;
+    GError       *inner_error = NULL;
+
+    information_buffer_offset = _mbim_message_get_information_buffer_offset (self);
+    tlv_list_offset = (guint64)information_buffer_offset + (guint64)relative_offset;
+
+    /* TLV list always at the end of the message */
+    if ((guint64)self->len < tlv_list_offset) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_MESSAGE,
+                     "cannot read TLV at offset (%u < %" G_GUINT64_FORMAT ")",
+                     self->len, tlv_list_offset);
+        return FALSE;
+    }
+
+    tlv_list_raw_size = self->len - (guint32)tlv_list_offset;
+    tlv_list_raw = (const guint8 *) G_STRUCT_MEMBER_P (self->data, tlv_list_offset);
+
+    while ((tlv_list_raw_size > 0) && !inner_error) {
+        MbimTlv *tlv;
+        guint32  tlv_size;
+
+        if (tlv_list_raw_size < sizeof (struct tlv)) {
+            g_warning ("Left %u bytes unused after the TLV list", tlv_list_raw_size);
+            break;
+        }
+
+        tlv = _mbim_tlv_new_from_raw (tlv_list_raw, tlv_list_raw_size, &tlv_size, &inner_error);
+        if (!tlv)
+            break;
+
+        list = g_list_append (list, tlv);
+        total_bytes_read += tlv_size;
+
+        g_assert (tlv_list_raw_size >= tlv_size);
+        tlv_list_raw += tlv_size;
+        tlv_list_raw_size -= tlv_size;
+    }
+
+    if (inner_error) {
+        g_propagate_error (error, inner_error);
+        g_list_free_full (list, (GDestroyNotify)mbim_tlv_unref);
+        return FALSE;
+    }
+
+    *bytes_read = total_bytes_read;
+    *tlv_list = list;
+    return TRUE;
+}
+
 /*****************************************************************************/
 /* Struct builder interface
  *
@@ -913,6 +1115,17 @@
 }
 
 void
+_mbim_struct_builder_append_guint16 (MbimStructBuilder *builder,
+                                     guint16            value)
+{
+    guint16 tmp;
+
+    /* guint16 values are added in the static buffer only */
+    tmp = GUINT16_TO_LE (value);
+    g_byte_array_append (builder->fixed_buffer, (guint8 *)&tmp, sizeof (tmp));
+}
+
+void
 _mbim_struct_builder_append_guint32 (MbimStructBuilder *builder,
                                      guint32            value)
 {
@@ -924,6 +1137,17 @@
 }
 
 void
+_mbim_struct_builder_append_gint32 (MbimStructBuilder *builder,
+                                    gint32             value)
+{
+    gint32 tmp;
+
+    /* gint32 values are added in the static buffer only */
+    tmp = GINT32_TO_LE (value);
+    g_byte_array_append (builder->fixed_buffer, (guint8 *)&tmp, sizeof (tmp));
+}
+
+void
 _mbim_struct_builder_append_guint32_array (MbimStructBuilder *builder,
                                            const guint32     *values,
                                            guint32            n_values)
@@ -1014,6 +1238,61 @@
 }
 
 void
+_mbim_struct_builder_append_string_tlv (MbimStructBuilder *builder,
+                                        const gchar       *value)
+{
+    guint8 reserved = 0;
+    guint8 padding = 0;
+    guint32 length;
+    gunichar2 *utf16 = NULL;
+    guint32 utf16_bytes = 0;
+    GError *error = NULL;
+
+    /* Add the reserved value */
+    g_byte_array_append (builder->fixed_buffer, (guint8 *)&reserved, sizeof (reserved));
+
+    /* Convert the string from UTF-8 to UTF-16HE */
+    if (value && value[0]) {
+        glong items_written = 0;
+        utf16 = g_utf8_to_utf16 (value,
+                                 -1,
+                                 NULL, /* bytes */
+                                 &items_written, /* gunichar2 */
+                                 &error);
+
+        if (!utf16) {
+            g_warning ("Error converting string: %s", error->message);
+            g_error_free (error);
+            return;
+        }
+        utf16_bytes = items_written * 2;
+
+        /* Add the padding value */
+        padding = utf16_bytes % 4;
+        g_byte_array_append (builder->fixed_buffer, (guint8 *)&padding, sizeof (padding));
+        g_debug ("padding:%d", padding);
+    }
+
+    /* Add the length value */
+    length = GUINT32_TO_LE (utf16_bytes);
+    g_byte_array_append (builder->fixed_buffer, (guint8 *)&length, sizeof (length));
+
+    /* And finally, the string itself to the variable buffer */
+    if (utf16_bytes) {
+        /* For BE systems, convert from BE to LE */
+        if (G_BYTE_ORDER == G_BIG_ENDIAN) {
+            guint i;
+
+            for (i = 0; i < (utf16_bytes / 2); i++)
+                utf16[i] = GUINT16_TO_LE (utf16[i]);
+        }
+        g_byte_array_append (builder->variable_buffer, (const guint8 *)utf16, (guint)utf16_bytes);
+        bytearray_apply_padding (builder->variable_buffer, &utf16_bytes);
+    }
+    g_free (utf16);
+}
+
+void
 _mbim_struct_builder_append_string_array (MbimStructBuilder  *builder,
                                           const gchar *const *values,
                                           guint32             n_values)
@@ -1172,6 +1451,13 @@
 }
 
 void
+_mbim_message_command_builder_append_guint16 (MbimMessageCommandBuilder *builder,
+                                              guint16                    value)
+{
+    _mbim_struct_builder_append_guint16 (builder->contents_builder, value);
+}
+
+void
 _mbim_message_command_builder_append_guint32_array (MbimMessageCommandBuilder *builder,
                                                     const guint32             *values,
                                                     guint32                    n_values)
@@ -1234,6 +1520,47 @@
 }
 
 /*****************************************************************************/
+/* TLVs only expected as primary message fields, not inside structs */
+
+void
+_mbim_message_command_builder_append_tlv (MbimMessageCommandBuilder *builder,
+                                          const MbimTlv             *tlv)
+{
+    const guint8 *raw_tlv;
+    guint32       raw_tlv_size;
+
+    raw_tlv = mbim_tlv_get_raw (tlv, &raw_tlv_size, NULL);
+    _mbim_struct_builder_append_byte_array (builder->contents_builder,
+                                            FALSE, FALSE, FALSE,
+                                            raw_tlv, raw_tlv_size,
+                                            FALSE);
+}
+
+void
+_mbim_message_command_builder_append_tlv_string (MbimMessageCommandBuilder *builder,
+                                                 const gchar               *str)
+{
+    g_autoptr(MbimTlv) tlv = NULL;
+    g_autoptr(GError)  error = NULL;
+
+    tlv = mbim_tlv_string_new (str, &error);
+    if (!tlv)
+        g_warning ("Error appending TLV: %s", error->message);
+    else
+        _mbim_message_command_builder_append_tlv (builder, tlv);
+}
+
+void
+_mbim_message_command_builder_append_tlv_list (MbimMessageCommandBuilder *builder,
+                                               const GList               *tlvs)
+{
+    const GList *l;
+
+    for (l = tlvs; l; l = g_list_next (l))
+        _mbim_message_command_builder_append_tlv (builder, (MbimTlv *)(l->data));
+}
+
+/*****************************************************************************/
 /* Generic message interface */
 
 MbimMessage *
@@ -1332,12 +1659,38 @@
                             const gchar       *line_prefix,
                             gboolean           headers_only)
 {
-    GString *printable;
-    MbimService service_read_fields = MBIM_SERVICE_INVALID;
+    gchar *str;
 
     g_return_val_if_fail (self != NULL, NULL);
     g_return_val_if_fail (line_prefix != NULL, NULL);
 
+    str = mbim_message_get_printable_full (self, 1, 0, line_prefix, headers_only, NULL);
+    g_assert (str);
+
+    return str;
+}
+
+gchar *
+mbim_message_get_printable_full (const MbimMessage  *self,
+                                 guint8              mbimex_version_major,
+                                 guint8              mbimex_version_minor,
+                                 const gchar        *line_prefix,
+                                 gboolean            headers_only,
+                                 GError            **error)
+{
+    GString     *printable = NULL;
+    MbimService  service_read_fields = MBIM_SERVICE_INVALID;
+
+    g_return_val_if_fail (self != NULL, NULL);
+    g_return_val_if_fail (line_prefix != NULL, NULL);
+
+    if (mbimex_version_major > 3) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                     "MBIMEx version %x.%02x is unsupported",
+                     mbimex_version_major, mbimex_version_minor);
+        return NULL;
+    }
+
     if (!line_prefix)
         line_prefix = "";
 
@@ -1398,14 +1751,14 @@
     case MBIM_MESSAGE_TYPE_HOST_ERROR:
     case MBIM_MESSAGE_TYPE_FUNCTION_ERROR:
         if (!headers_only) {
-            MbimProtocolError error;
+            MbimProtocolError protocol_error;
 
-            error = mbim_message_error_get_error_status_code (self);
+            protocol_error = mbim_message_error_get_error_status_code (self);
             g_string_append_printf (printable,
                                     "%sContents:\n"
                                     "%s  error = '%s' (0x%08x)\n",
                                     line_prefix,
-                                    line_prefix, mbim_protocol_error_get_string (error), error);
+                                    line_prefix, mbim_protocol_error_get_string (protocol_error), protocol_error);
         }
         break;
 
@@ -1502,59 +1855,103 @@
 
     if (service_read_fields != MBIM_SERVICE_INVALID) {
         g_autofree gchar  *fields_printable = NULL;
-        g_autoptr(GError)  error = NULL;
+        g_autoptr(GError)  inner_error = NULL;
 
         switch (service_read_fields) {
         case MBIM_SERVICE_BASIC_CONNECT:
-            fields_printable = __mbim_message_basic_connect_get_printable_fields (self, line_prefix, &error);
+            if (mbimex_version_major < 2)
+                fields_printable = __mbim_message_basic_connect_get_printable_fields (self, line_prefix, &inner_error);
+            else if (mbimex_version_major == 2) {
+                fields_printable = __mbim_message_ms_basic_connect_v2_get_printable_fields (self, line_prefix, &inner_error);
+                /* attempt fallback to v1 printable */
+                if (g_error_matches (inner_error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_UNSUPPORTED)) {
+                    g_clear_error (&inner_error);
+                    fields_printable = __mbim_message_basic_connect_get_printable_fields (self, line_prefix, &inner_error);
+                }
+            } else if (mbimex_version_major == 3) {
+                fields_printable = __mbim_message_ms_basic_connect_v3_get_printable_fields (self, line_prefix, &inner_error);
+                /* attempt fallback to v2 printable */
+                if (g_error_matches (inner_error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_UNSUPPORTED)) {
+                    g_clear_error (&inner_error);
+                    fields_printable = __mbim_message_ms_basic_connect_v2_get_printable_fields (self, line_prefix, &inner_error);
+                    /* attempt fallback to v1 printable */
+                    if (g_error_matches (inner_error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_UNSUPPORTED)) {
+                        g_clear_error (&inner_error);
+                        fields_printable = __mbim_message_basic_connect_get_printable_fields (self, line_prefix, &inner_error);
+                    }
+                }
+            } else
+                g_assert_not_reached ();
             break;
         case MBIM_SERVICE_SMS:
-            fields_printable = __mbim_message_sms_get_printable_fields (self, line_prefix, &error);
+            fields_printable = __mbim_message_sms_get_printable_fields (self, line_prefix, &inner_error);
             break;
         case MBIM_SERVICE_USSD:
-            fields_printable = __mbim_message_ussd_get_printable_fields (self, line_prefix, &error);
+            fields_printable = __mbim_message_ussd_get_printable_fields (self, line_prefix, &inner_error);
             break;
         case MBIM_SERVICE_PHONEBOOK:
-            fields_printable = __mbim_message_phonebook_get_printable_fields (self, line_prefix, &error);
+            fields_printable = __mbim_message_phonebook_get_printable_fields (self, line_prefix, &inner_error);
             break;
         case MBIM_SERVICE_STK:
-            fields_printable = __mbim_message_stk_get_printable_fields (self, line_prefix, &error);
+            fields_printable = __mbim_message_stk_get_printable_fields (self, line_prefix, &inner_error);
             break;
         case MBIM_SERVICE_AUTH:
-            fields_printable = __mbim_message_auth_get_printable_fields (self, line_prefix, &error);
+            fields_printable = __mbim_message_auth_get_printable_fields (self, line_prefix, &inner_error);
             break;
         case MBIM_SERVICE_DSS:
-            fields_printable = __mbim_message_dss_get_printable_fields (self, line_prefix, &error);
+            fields_printable = __mbim_message_dss_get_printable_fields (self, line_prefix, &inner_error);
             break;
         case MBIM_SERVICE_MS_FIRMWARE_ID:
-            fields_printable = __mbim_message_ms_firmware_id_get_printable_fields (self, line_prefix, &error);
+            fields_printable = __mbim_message_ms_firmware_id_get_printable_fields (self, line_prefix, &inner_error);
             break;
         case MBIM_SERVICE_MS_HOST_SHUTDOWN:
-            fields_printable = __mbim_message_ms_host_shutdown_get_printable_fields (self, line_prefix, &error);
+            fields_printable = __mbim_message_ms_host_shutdown_get_printable_fields (self, line_prefix, &inner_error);
             break;
         case MBIM_SERVICE_MS_SAR:
-            fields_printable = __mbim_message_ms_sar_get_printable_fields (self, line_prefix, &error);
+            fields_printable = __mbim_message_ms_sar_get_printable_fields (self, line_prefix, &inner_error);
             break;
         case MBIM_SERVICE_PROXY_CONTROL:
-            fields_printable = __mbim_message_proxy_control_get_printable_fields (self, line_prefix, &error);
+            fields_printable = __mbim_message_proxy_control_get_printable_fields (self, line_prefix, &inner_error);
             break;
         case MBIM_SERVICE_QMI:
-            fields_printable = __mbim_message_qmi_get_printable_fields (self, line_prefix, &error);
+            fields_printable = __mbim_message_qmi_get_printable_fields (self, line_prefix, &inner_error);
             break;
         case MBIM_SERVICE_ATDS:
-            fields_printable = __mbim_message_atds_get_printable_fields (self, line_prefix, &error);
+            fields_printable = __mbim_message_atds_get_printable_fields (self, line_prefix, &inner_error);
             break;
         case MBIM_SERVICE_INTEL_FIRMWARE_UPDATE:
-            fields_printable = __mbim_message_intel_firmware_update_get_printable_fields (self, line_prefix, &error);
+            fields_printable = __mbim_message_intel_firmware_update_get_printable_fields (self, line_prefix, &inner_error);
             break;
         case MBIM_SERVICE_QDU:
-            fields_printable = __mbim_message_qdu_get_printable_fields (self, line_prefix, &error);
+            fields_printable = __mbim_message_qdu_get_printable_fields (self, line_prefix, &inner_error);
             break;
         case MBIM_SERVICE_MS_BASIC_CONNECT_EXTENSIONS:
-            fields_printable = __mbim_message_ms_basic_connect_extensions_get_printable_fields (self, line_prefix, &error);
+            if (mbimex_version_major < 2)
+                fields_printable = __mbim_message_ms_basic_connect_extensions_get_printable_fields (self, line_prefix, &inner_error);
+            else if (mbimex_version_major == 2) {
+                fields_printable = __mbim_message_ms_basic_connect_extensions_v2_get_printable_fields (self, line_prefix, &inner_error);
+                /* attempt fallback to v1 printable */
+                if (g_error_matches (inner_error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_UNSUPPORTED)) {
+                    g_clear_error (&inner_error);
+                    fields_printable = __mbim_message_ms_basic_connect_extensions_get_printable_fields (self, line_prefix, &inner_error);
+                }
+            } else if (mbimex_version_major == 3) {
+                fields_printable = __mbim_message_ms_basic_connect_extensions_v3_get_printable_fields (self, line_prefix, &inner_error);
+                /* attempt fallback to v2 printable */
+                if (g_error_matches (inner_error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_UNSUPPORTED)) {
+                    g_clear_error (&inner_error);
+                    fields_printable = __mbim_message_ms_basic_connect_extensions_v2_get_printable_fields (self, line_prefix, &inner_error);
+                    /* attempt fallback to v1 printable */
+                    if (g_error_matches (inner_error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_UNSUPPORTED)) {
+                        g_clear_error (&inner_error);
+                        fields_printable = __mbim_message_ms_basic_connect_extensions_get_printable_fields (self, line_prefix, &inner_error);
+                    }
+                }
+             } else
+               g_assert_not_reached ();
             break;
         case MBIM_SERVICE_MS_UICC_LOW_LEVEL_ACCESS:
-            fields_printable = __mbim_message_ms_uicc_low_level_access_get_printable_fields (self, line_prefix, &error);
+            fields_printable = __mbim_message_ms_uicc_low_level_access_get_printable_fields (self, line_prefix, &inner_error);
             break;
         case MBIM_SERVICE_INVALID:
         case MBIM_SERVICE_LAST:
@@ -1563,10 +1960,10 @@
             break;
         }
 
-        if (error)
+        if (inner_error)
             g_string_append_printf (printable,
                                     "%sFields: %s\n",
-                                    line_prefix, error->message);
+                                    line_prefix, inner_error->message);
         else if (fields_printable && fields_printable[0])
             g_string_append_printf (printable,
                                     "%sFields:\n"
diff --git a/src/libmbim-glib/mbim-message.h b/src/libmbim-glib/mbim-message.h
index 571d45a..86bd9b0 100644
--- a/src/libmbim-glib/mbim-message.h
+++ b/src/libmbim-glib/mbim-message.h
@@ -160,6 +160,10 @@
  *
  * Gets a printable string with the contents of the whole MBIM message.
  *
+ * This method will not fail if the parsing of the message contents fails,
+ * a fallback text with the error will be included in the generated printable
+ * information instead.
+ *
  * Returns: a newly allocated string, which should be freed with g_free().
  *
  * Since: 1.0
@@ -169,6 +173,43 @@
                                    gboolean            headers_only);
 
 /**
+ * mbim_message_get_printable_full:
+ * @self: a #MbimMessage.
+ * @mbimex_version_major: major version of the agreed MBIMEx support.
+ * @mbimex_version_minor: minor version of the agreed MBIMEx support.
+ * @line_prefix: prefix string to use in each new generated line.
+ * @headers_only: %TRUE if only basic headers should be printed.
+ * @error: return location for error or %NULL.
+ *
+ * Gets a printable string with the contents of the whole MBIM message.
+ *
+ * Unlike mbim_message_get_printable(), this method allows specifying the
+ * MBIMEx version agreed between host and device, so that the correct
+ * processing and parsing is done on messages in the newer MBIMEx versions.
+ *
+ * If @mbimex_version_major < 2, this method behaves exactly as
+ * mbim_message_get_printable().
+ *
+ * If the specified @mbimex_version_major is unsupported, an error will be
+ * returned.
+ *
+ * This method will not fail if the parsing of the message contents fails,
+ * a fallback text with the error will be included in the generated printable
+ * information instead.
+ *
+ * Returns: a newly allocated string which should be freed with g_free(), or
+ * #NULL if @error is set.
+ *
+ * Since: 1.28
+ */
+gchar *mbim_message_get_printable_full (const MbimMessage  *self,
+                                        guint8              mbimex_version_major,
+                                        guint8              mbimex_version_minor,
+                                        const gchar        *line_prefix,
+                                        gboolean            headers_only,
+                                        GError            **error);
+
+/**
  * mbim_message_get_raw:
  * @self: a #MbimMessage.
  * @length: (out): return location for the size of the output buffer.
diff --git a/src/libmbim-glib/mbim-proxy.c b/src/libmbim-glib/mbim-proxy.c
index 084b255..dcbed96 100644
--- a/src/libmbim-glib/mbim-proxy.c
+++ b/src/libmbim-glib/mbim-proxy.c
@@ -27,6 +27,7 @@
 #include "mbim-enum-types.h"
 #include "mbim-error-types.h"
 #include "mbim-basic-connect.h"
+#include "mbim-ms-basic-connect-extensions.h"
 #include "mbim-proxy-helpers.h"
 
 /* The mbim-proxy may be used for bulk data transfer, such as modem
@@ -36,6 +37,10 @@
  */
 #define BUFFER_SIZE 4096
 
+/* The proxy control "Version" indication reporting the last agreed
+ * MBIMEx version, if any */
+#define MBIM_DEVICE_PROXY_CONTROL_VERSION "mbim-device-proxy-control-version"
+
 G_DEFINE_TYPE (MbimProxy, mbim_proxy, G_TYPE_OBJECT)
 
 enum {
@@ -589,8 +594,9 @@
                              Client      *client,
                              MbimMessage *message)
 {
-    Request *request;
-    MbimStatusError status = MBIM_STATUS_ERROR_FAILURE;
+    Request                *request;
+    MbimStatusError         status = MBIM_STATUS_ERROR_FAILURE;
+    g_autoptr(MbimMessage)  indication = NULL;
 
     /* create request holder */
     request = request_new (self, client, message);
@@ -659,7 +665,8 @@
                                          GAsyncResult *res,
                                          Request      *request)
 {
-    g_autoptr(GError) error = NULL;
+    g_autoptr(GError)  error = NULL;
+    MbimMessage       *indication;
 
     if (!internal_device_open_finish (self, res, &error)) {
         g_warning ("[client %lu,0x%08x] cannot configure proxy: couldn't open MBIM device: %s",
@@ -673,6 +680,15 @@
     g_debug ("[client %lu,0x%08x] proxy configured",
              request->client->id, request->original_transaction_id);
 
+    /* notify the client about the MBIMEx version */
+    indication = (MbimMessage *) g_object_get_data (G_OBJECT (request->client->device), MBIM_DEVICE_PROXY_CONTROL_VERSION);
+    if (indication) {
+        if (!client_send_message (request->client, indication, &error))
+            g_warning ("[client %lu] couldn't report MBIMEx version update: %s", request->client->id, error->message);
+        else
+            g_debug ("[client %lu] reported MBIMEx version update", request->client->id);
+    }
+
     if (request->client->config_ongoing == TRUE)
         request->client->config_ongoing = FALSE;
     request->response = build_proxy_control_command_done (request->message, MBIM_STATUS_ERROR_NONE);
@@ -959,6 +975,94 @@
 }
 
 /*****************************************************************************/
+/* MBIMEx version detection */
+
+static MbimMessage *
+build_proxy_control_version_notification (guint16 mbim_version,
+                                          guint16 ms_mbimex_version)
+{
+    MbimMessage                    *message;
+    struct indicate_status_message *indicate_status;
+    guint16                         tmp;
+    guint                           buffer_i = 0;
+    gsize                           buffer_length = 0;
+
+    buffer_length = sizeof (mbim_version) + sizeof (ms_mbimex_version);
+
+    message = (MbimMessage *) _mbim_message_allocate (MBIM_MESSAGE_TYPE_INDICATE_STATUS,
+                                                      0,
+                                                      sizeof (struct indicate_status_message) + buffer_length);
+    indicate_status = &(((struct full_message *)(message->data))->message.indicate_status);
+    indicate_status->fragment_header.total   = GUINT32_TO_LE (1);
+    indicate_status->fragment_header.current = 0;
+    memcpy (indicate_status->service_id, MBIM_UUID_PROXY_CONTROL, sizeof (MbimUuid));
+    indicate_status->command_id  = GUINT32_TO_LE (MBIM_CID_PROXY_CONTROL_VERSION);
+    indicate_status->buffer_length = GUINT32_TO_LE (buffer_length);
+
+    tmp = GUINT16_TO_LE (mbim_version);
+    memcpy (&indicate_status->buffer[buffer_i], &tmp, sizeof (tmp));
+    buffer_i += sizeof (tmp);
+    tmp = GUINT16_TO_LE (ms_mbimex_version);
+    memcpy (&indicate_status->buffer[buffer_i], &tmp, sizeof (tmp));
+    buffer_i += sizeof (tmp);
+    g_assert (buffer_i == buffer_length);
+
+    return message;
+}
+
+static void
+monitor_ms_basic_connect_extensions_version_response (MbimProxy   *self,
+                                                      MbimDevice  *device,
+                                                      MbimMessage *response)
+{
+    g_autoptr(MbimMessage)  indication = NULL;
+    guint16                 mbim_version;
+    guint16                 ms_mbimex_version;
+    guint8                  ms_mbimex_version_major;
+    guint8                  ms_mbimex_version_minor;
+    GList                  *l;
+
+    /* monitor the MBIMEx version agreed between the clients and the device */
+    if (!mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, NULL) ||
+        (mbim_message_command_done_get_service (response) != MBIM_SERVICE_MS_BASIC_CONNECT_EXTENSIONS) ||
+        (mbim_message_command_done_get_cid (response) != MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_VERSION) ||
+        !mbim_message_ms_basic_connect_extensions_v2_version_response_parse (response, &mbim_version, &ms_mbimex_version, NULL))
+        return;
+
+    ms_mbimex_version_major = ms_mbimex_version >> 8;
+    ms_mbimex_version_minor = ms_mbimex_version & 0xFF;
+
+    g_message ("Proxy monitoring detected MBIMEx version agreed with device: %x.%02x",
+               ms_mbimex_version_major, ms_mbimex_version_minor);
+    mbim_device_set_ms_mbimex_version (device, ms_mbimex_version_major, ms_mbimex_version_minor, NULL);
+
+    /* notify to all clients about the MBIMEx version update */
+    indication = build_proxy_control_version_notification (mbim_version, ms_mbimex_version);
+    for (l = self->priv->clients; l; l = g_list_next (l)) {
+        g_autoptr(GError)  error = NULL;
+        Client            *client;
+
+        client = l->data;
+        if (client->device != device)
+            continue;
+
+        if (!client_send_message (client, indication, &error))
+            g_warning ("[client %lu] couldn't report MBIMEx version update to %x.%02x: %s",
+                       client->id, ms_mbimex_version_major, ms_mbimex_version_minor, error->message);
+        else
+            g_debug ("[client %lu] reported MBIMEx version update to %x.%02x",
+                     client->id, ms_mbimex_version_major, ms_mbimex_version_minor);
+    }
+
+    /* the indication is stored as data associated to the device, so that it can
+     * be reused any time new clients attempt an open */
+    g_object_set_data_full (G_OBJECT (device),
+                            MBIM_DEVICE_PROXY_CONTROL_VERSION,
+                            mbim_message_ref (indication),
+                            (GDestroyNotify)mbim_message_unref);
+}
+
+/*****************************************************************************/
 /* Standard command */
 
 static void
@@ -989,6 +1093,10 @@
     /* replace reponse transaction id with the requested transaction id */
     g_debug ("[client %lu,0x%08x] response from device received",
              request->client->id, request->original_transaction_id);
+
+    /* try to match the MBIMEx version exchange */
+    monitor_ms_basic_connect_extensions_version_response (request->self, device, request->response);
+
     mbim_message_set_transaction_id (request->response, request->original_transaction_id);
     request_complete_and_free (request);
 }
diff --git a/src/libmbim-glib/mbim-tlv-private.h b/src/libmbim-glib/mbim-tlv-private.h
new file mode 100644
index 0000000..7dc2d8f
--- /dev/null
+++ b/src/libmbim-glib/mbim-tlv-private.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Intel Corporation
+ *
+ * This is a private non-installed header
+ */
+
+#ifndef _LIBMBIM_GLIB_MBIM_TLV_PRIVATE_H_
+#define _LIBMBIM_GLIB_MBIM_TLV_PRIVATE_H_
+
+#if !defined (LIBMBIM_GLIB_COMPILATION)
+#error "This is a private header!!"
+#endif
+
+#include <glib.h>
+
+#include "mbim-tlv.h"
+
+G_BEGIN_DECLS
+
+/*****************************************************************************/
+/* The MbimTlv */
+
+/* Defined in the same way as GByteArray */
+struct _MbimTlv {
+  guint8 *data;
+  guint   len;
+};
+
+struct tlv {
+  guint16 type;
+  guint8  reserved;
+  guint8  padding_length;
+  guint32 data_length;
+  guint8  data[];
+} __attribute__((packed));
+
+#define MBIM_TLV_HEADER(self) ((struct tlv *)(((MbimTlv *)self)->data))
+
+#define MBIM_TLV_FIELD_TYPE(self)           MBIM_TLV_HEADER (self)->type
+#define MBIM_TLV_FIELD_RESERVED(self)       MBIM_TLV_HEADER (self)->reserved
+#define MBIM_TLV_FIELD_PADDING_LENGTH(self) MBIM_TLV_HEADER (self)->padding_length
+#define MBIM_TLV_FIELD_DATA_LENGTH(self)    MBIM_TLV_HEADER (self)->data_length
+#define MBIM_TLV_FIELD_DATA(self)           MBIM_TLV_HEADER (self)->data
+
+#define MBIM_TLV_GET_TLV_TYPE(self)    (MbimTlvType) GUINT16_FROM_LE (MBIM_TLV_FIELD_TYPE (self))
+#define MBIM_TLV_GET_DATA_LENGTH(self) GUINT32_FROM_LE (MBIM_TLV_FIELD_DATA_LENGTH (self))
+
+/*****************************************************************************/
+/* Print support */
+
+gchar *_mbim_tlv_print (const MbimTlv *tlv,
+                        const gchar   *line_prefix);
+
+/*****************************************************************************/
+/* Parsing support */
+
+MbimTlv *_mbim_tlv_new_from_raw (const guint8  *raw,
+                                 guint32        raw_length,
+                                 guint32       *bytes_read,
+                                 GError       **error);
+
+G_END_DECLS
+
+#endif /* _LIBMBIM_GLIB_MBIM_TLV_PRIVATE_H_ */
diff --git a/src/libmbim-glib/mbim-tlv.c b/src/libmbim-glib/mbim-tlv.c
new file mode 100644
index 0000000..8058836
--- /dev/null
+++ b/src/libmbim-glib/mbim-tlv.c
@@ -0,0 +1,476 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Intel Corporation
+ */
+
+#include <glib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <endian.h>
+
+#include "mbim-tlv.h"
+#include "mbim-tlv-private.h"
+#include "mbim-error-types.h"
+#include "mbim-enum-types.h"
+#include "mbim-common.h"
+
+/*****************************************************************************/
+
+GType
+mbim_tlv_get_type (void)
+{
+    static gsize g_define_type_id_initialized = 0;
+
+    if (g_once_init_enter (&g_define_type_id_initialized)) {
+        GType g_define_type_id =
+            g_boxed_type_register_static (g_intern_static_string ("MbimTlv"),
+                                          (GBoxedCopyFunc) mbim_tlv_ref,
+                                          (GBoxedFreeFunc) mbim_tlv_unref);
+
+        g_once_init_leave (&g_define_type_id_initialized, g_define_type_id);
+    }
+
+    return g_define_type_id_initialized;
+}
+
+/*****************************************************************************/
+
+gchar *
+_mbim_tlv_print (const MbimTlv *tlv,
+                 const gchar   *line_prefix)
+{
+    GString          *str;
+    MbimTlvType       tlv_type;
+    const gchar      *tlv_type_str;
+    const guint8     *tlv_data;
+    guint32           tlv_data_size;
+    g_autofree gchar *tlv_data_str = NULL;
+
+    tlv_type = mbim_tlv_get_tlv_type (tlv);
+    tlv_type_str = mbim_tlv_type_get_string (tlv_type);
+
+    str = g_string_new ("");
+    g_string_append_printf (str, "{\n");
+    g_string_append_printf (str, "%s  tlv type   = %s (0x%04x)\n", line_prefix, tlv_type_str ? tlv_type_str : "unknown", tlv_type);
+
+    tlv_data = mbim_tlv_get_tlv_data (tlv, &tlv_data_size);
+    tlv_data_str = mbim_common_str_hex (tlv_data, tlv_data_size, ':');
+    g_string_append_printf (str, "%s  tlv data   = %s\n", line_prefix, tlv_data_str ? tlv_data_str : "");
+
+    if (tlv_type == MBIM_TLV_TYPE_WCHAR_STR) {
+        g_autoptr(GError) error = NULL;
+        g_autofree gchar *tlv_data_string_str = NULL;
+
+        tlv_data_string_str = mbim_tlv_string_get (tlv, &error);
+        if (!tlv_data_string_str)
+            tlv_data_string_str = g_strdup_printf ("*** error: %s", error->message);
+        g_string_append_printf (str, "%s  tlv string = %s\n", line_prefix, tlv_data_string_str ? tlv_data_string_str : "");
+    } else if (tlv_type == MBIM_TLV_TYPE_UINT16_TBL) {
+        g_autoptr(GError)   error = NULL;
+        guint32             array_size = 0;
+        g_autofree guint16 *array = NULL;
+        g_autofree gchar   *tlv_data_string_str = NULL;
+
+        if (!mbim_tlv_guint16_array_get (tlv, &array_size, &array, &error))
+            tlv_data_string_str = g_strdup_printf ("*** error: %s", error->message);
+        else {
+            GString *aux;
+            guint32  i;
+
+            aux = g_string_new ("[");
+            for (i = 0; i < array_size; i++)
+                g_string_append_printf (aux, "%s%" G_GUINT16_FORMAT, (i == 0) ? "" : ",", array[i]);
+            g_string_append (aux, "]");
+            tlv_data_string_str = g_string_free (aux, FALSE);
+        }
+        g_string_append_printf (str, "%s  tlv uint16 array = %s\n", line_prefix, tlv_data_string_str ? tlv_data_string_str : "");
+    }
+
+    g_string_append_printf (str, "%s}", line_prefix);
+
+    return g_string_free (str, FALSE);
+}
+
+/*****************************************************************************/
+
+MbimTlv *
+mbim_tlv_new (MbimTlvType   tlv_type,
+              const guint8 *tlv_data,
+              guint32       tlv_data_length)
+{
+    GByteArray *self;
+    guint32     tlv_size;
+    guint32     padding_size;
+
+    g_return_val_if_fail (tlv_type != MBIM_TLV_TYPE_INVALID, NULL);
+
+    /* Compute size of the TLV and allocate heap for it */
+    padding_size = (tlv_data_length % 4) ? (4 - (tlv_data_length % 4)) : 0;
+    tlv_size = sizeof (struct tlv) + tlv_data_length + padding_size;
+    self = g_byte_array_sized_new (tlv_size);
+    g_byte_array_set_size (self, tlv_size);
+
+    /* Set TLV header */
+    MBIM_TLV_FIELD_TYPE (self)           = GUINT16_TO_LE (tlv_type);
+    MBIM_TLV_FIELD_RESERVED (self)       = 0;
+    MBIM_TLV_FIELD_PADDING_LENGTH (self) = padding_size;
+    MBIM_TLV_FIELD_DATA_LENGTH (self)    = GUINT32_TO_LE (tlv_data_length);
+
+    if (tlv_data && tlv_data_length) {
+        memcpy (MBIM_TLV_FIELD_DATA (self), tlv_data, tlv_data_length);
+        if (padding_size)
+            memset (MBIM_TLV_FIELD_DATA (self) + tlv_data_length, 0, padding_size);
+    }
+
+    return (MbimTlv *)self;
+}
+
+MbimTlv *
+_mbim_tlv_new_from_raw (const guint8  *raw,
+                        guint32        raw_length,
+                        guint32       *bytes_read,
+                        GError       **error)
+{
+    guint32 tlv_size;
+
+    g_assert (raw_length >= sizeof (struct tlv));
+    tlv_size = sizeof (struct tlv) + GUINT32_FROM_LE (((struct tlv *)raw)->data_length) + ((struct tlv *)raw)->padding_length;
+
+    *bytes_read = tlv_size;
+    return (MbimTlv *) g_byte_array_append (g_byte_array_sized_new (tlv_size), raw, tlv_size);
+}
+
+MbimTlv *
+mbim_tlv_dup (const MbimTlv *self)
+{
+    g_return_val_if_fail (self != NULL, NULL);
+
+    return mbim_tlv_new (MBIM_TLV_GET_TLV_TYPE (self),
+                         MBIM_TLV_FIELD_DATA (self),
+                         MBIM_TLV_GET_DATA_LENGTH (self));
+}
+
+MbimTlv *
+mbim_tlv_ref (MbimTlv *self)
+{
+    g_return_val_if_fail (self != NULL, NULL);
+
+    return (MbimTlv *) g_byte_array_ref ((GByteArray *)self);
+}
+
+void
+mbim_tlv_unref (MbimTlv *self)
+{
+    g_return_if_fail (self != NULL);
+
+    g_byte_array_unref ((GByteArray *)self);
+}
+
+MbimTlvType
+mbim_tlv_get_tlv_type (const MbimTlv *self)
+{
+    g_return_val_if_fail (self != NULL, MBIM_TLV_TYPE_INVALID);
+
+    return MBIM_TLV_GET_TLV_TYPE (self);
+}
+
+const guint8 *
+mbim_tlv_get_tlv_data (const MbimTlv *self,
+                       guint32       *out_length)
+{
+    g_return_val_if_fail (self != NULL, NULL);
+    g_return_val_if_fail (out_length != NULL, NULL);
+
+    *out_length = MBIM_TLV_GET_DATA_LENGTH (self);
+    return MBIM_TLV_FIELD_DATA (self);
+}
+
+const guint8 *
+mbim_tlv_get_raw (const MbimTlv  *self,
+                  guint32        *length,
+                  GError        **error)
+{
+    g_return_val_if_fail (self != NULL, NULL);
+    g_return_val_if_fail (length != NULL, NULL);
+
+    if (!self->data || !self->len) {
+        g_set_error_literal (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED, "TLV is invalid");
+        return NULL;
+    }
+
+    *length = (guint32) self->len;
+    return self->data;
+}
+
+/*****************************************************************************/
+
+MbimTlv *
+mbim_tlv_string_new (const gchar  *str,
+                     GError      **error)
+{
+    g_autofree gunichar2 *utf16 = NULL;
+    guint32               utf16_bytes = 0;
+
+    /* Convert the string from UTF-8 to UTF-16HE */
+    if (str && str[0]) {
+        glong items_written = 0;
+
+        utf16 = g_utf8_to_utf16 (str,
+                                 -1,
+                                 NULL, /* bytes */
+                                 &items_written, /* gunichar2 */
+                                 error);
+        if (!utf16)
+            return NULL;
+
+        utf16_bytes = items_written * 2;
+
+        /* For BE systems, convert from BE to LE */
+        if (G_BYTE_ORDER == G_BIG_ENDIAN) {
+            guint i;
+
+            for (i = 0; i < items_written; i++)
+                utf16[i] = GUINT16_TO_LE (utf16[i]);
+        }
+    }
+
+    return mbim_tlv_new (MBIM_TLV_TYPE_WCHAR_STR, (const guint8 *)utf16, utf16_bytes);
+}
+
+gchar *
+mbim_tlv_string_get (const MbimTlv  *self,
+                     GError        **error)
+{
+    const gunichar2      *utf16 = NULL;
+    g_autofree gunichar2 *utf16d = NULL;
+    guint32               size;
+
+    g_return_val_if_fail (self != NULL, NULL);
+
+    if (MBIM_TLV_GET_TLV_TYPE (self) != MBIM_TLV_TYPE_WCHAR_STR) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                     "TLV is not a WCHAR string");
+        return NULL;
+    }
+
+    utf16 = (const gunichar2 *) MBIM_TLV_FIELD_DATA (self);
+    size = MBIM_TLV_GET_DATA_LENGTH (self);
+
+    /* For BE systems, convert from LE to BE */
+    if (G_BYTE_ORDER == G_BIG_ENDIAN) {
+        guint i;
+
+        utf16d = (gunichar2 *) g_malloc (size);
+        for (i = 0; i < (size / 2); i++)
+            utf16d[i] = GUINT16_FROM_LE (utf16[i]);
+    }
+
+    return g_utf16_to_utf8 (utf16d ? utf16d : utf16,
+                            size / 2,
+                            NULL,
+                            NULL,
+                            error);
+}
+
+/*****************************************************************************/
+
+gboolean
+mbim_tlv_guint16_array_get (const MbimTlv  *self,
+                            guint32        *array_size,
+                            guint16       **array,
+                            GError        **error)
+{
+    guint32             size;
+    g_autofree guint16 *tmp = NULL;
+
+    g_return_val_if_fail (self != NULL, FALSE);
+
+    if (MBIM_TLV_GET_TLV_TYPE (self) != MBIM_TLV_TYPE_UINT16_TBL) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                     "TLV is not a UINT16 array");
+        return FALSE;
+    }
+
+    size = MBIM_TLV_GET_DATA_LENGTH (self);
+    if (size % 2 != 0) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                     "Invalid TLV data length, must be multiple of 2: %u",
+                     size);
+        return FALSE;
+    }
+
+    if (size) {
+        tmp = (guint16 *) g_memdup ((const guint16 *)MBIM_TLV_FIELD_DATA (self), size);
+
+        /* For BE systems, convert from LE to BE */
+        if (G_BYTE_ORDER == G_BIG_ENDIAN) {
+            guint i;
+
+            for (i = 0; i < (size / 2); i++)
+                tmp[i] = GUINT16_FROM_LE (tmp[i]);
+        }
+    }
+
+    if (array_size)
+        *array_size = size / 2;
+    if (array)
+        *array = g_steal_pointer (&tmp);
+
+    return TRUE;
+}
+
+/*****************************************************************************/
+
+gboolean
+mbim_tlv_wake_command_get (const MbimTlv   *self,
+                           const MbimUuid **service,
+                           guint32         *cid,
+                           guint32         *payload_size,
+                           guint8         **payload,
+                           GError         **error)
+{
+    const guint8 *tlv_data;
+    guint32       tlv_data_size;
+    guint32       buffer_offset;
+    guint32       buffer_size;
+    guint32       offset = 0;
+    guint64       required_size;
+
+    g_return_val_if_fail (self != NULL, FALSE);
+
+    if (MBIM_TLV_GET_TLV_TYPE (self) != MBIM_TLV_TYPE_WAKE_COMMAND) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                     "TLV is not a wake command");
+        return FALSE;
+    }
+
+    tlv_data = mbim_tlv_get_tlv_data (self, &tlv_data_size);
+    tlv_data_size = MBIM_TLV_GET_DATA_LENGTH (self);
+
+    required_size = 28;
+    if (tlv_data_size < required_size) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_MESSAGE,
+                     "cannot read wake command TLV (%u < %" G_GUINT64_FORMAT ")",
+                     tlv_data_size, required_size);
+        return FALSE;
+    }
+
+    if (service)
+        *service = (const MbimUuid *) G_STRUCT_MEMBER_P (tlv_data, offset);
+    offset += 16;
+
+    if (cid)
+        *cid = GUINT32_FROM_LE (G_STRUCT_MEMBER (guint32, tlv_data, offset));
+    offset += 4;
+
+    buffer_offset = GUINT32_FROM_LE (G_STRUCT_MEMBER (guint32, tlv_data, offset));
+    offset += 4;
+
+    buffer_size = GUINT32_FROM_LE (G_STRUCT_MEMBER (guint32, tlv_data, offset));
+    offset += 4;
+
+    if (buffer_size > 0) {
+        if (buffer_offset != required_size) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_MESSAGE,
+                         "cannot read wake command TLV: invalid payload offset (%u)",
+                         buffer_offset);
+            return FALSE;
+        }
+
+        required_size += buffer_size;
+        if (tlv_data_size < required_size) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_MESSAGE,
+                         "cannot read wake command TLV payload (%u bytes) (%u < %" G_GUINT64_FORMAT ")",
+                         buffer_size, tlv_data_size, required_size);
+            return FALSE;
+        }
+    }
+
+    if (payload_size)
+        *payload_size = buffer_size;
+    if (payload)
+        *payload = (buffer_size ? g_memdup (&tlv_data[offset], buffer_size) : NULL);
+
+    return TRUE;
+}
+
+/*****************************************************************************/
+
+gboolean
+mbim_tlv_wake_packet_get (const MbimTlv  *self,
+                          guint32        *filter_id,
+                          guint32        *original_packet_size,
+                          guint32        *packet_size,
+                          guint8        **packet,
+                          GError        **error)
+{
+
+    const guint8 *tlv_data;
+    guint32       tlv_data_size;
+    guint32       buffer_offset;
+    guint32       buffer_size;
+    guint32       offset = 0;
+    guint64       required_size;
+
+    g_return_val_if_fail (self != NULL, FALSE);
+
+    if (MBIM_TLV_GET_TLV_TYPE (self) != MBIM_TLV_TYPE_WAKE_PACKET) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                     "TLV is not a wake packet");
+        return FALSE;
+    }
+
+    tlv_data = mbim_tlv_get_tlv_data (self, &tlv_data_size);
+    tlv_data_size = MBIM_TLV_GET_DATA_LENGTH (self);
+
+    required_size = 16;
+    if (tlv_data_size < required_size) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_MESSAGE,
+                     "cannot read wake packet TLV (%u < %" G_GUINT64_FORMAT ")",
+                     tlv_data_size, required_size);
+        return FALSE;
+    }
+
+    if (filter_id)
+        *filter_id = GUINT32_FROM_LE (G_STRUCT_MEMBER (guint32, tlv_data, offset));
+    offset += 4;
+
+    if (original_packet_size)
+        *original_packet_size = GUINT32_FROM_LE (G_STRUCT_MEMBER (guint32, tlv_data, offset));
+    offset += 4;
+
+    buffer_offset = GUINT32_FROM_LE (G_STRUCT_MEMBER (guint32, tlv_data, offset));
+    offset += 4;
+
+    buffer_size = GUINT32_FROM_LE (G_STRUCT_MEMBER (guint32, tlv_data, offset));
+    offset += 4;
+
+    if (buffer_size > 0) {
+        if (buffer_offset != offset) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_MESSAGE,
+                         "cannot read wake packet TLV: invalid saved packet offset (%u)",
+                         buffer_offset);
+            return FALSE;
+        }
+
+        required_size += buffer_size;
+        if (tlv_data_size < required_size) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_MESSAGE,
+                         "cannot read wake packet TLV payload (%u bytes) (%u < %" G_GUINT64_FORMAT ")",
+                         buffer_size, tlv_data_size, required_size);
+            return FALSE;
+        }
+    }
+
+    if (packet_size)
+        *packet_size = buffer_size;
+    if (packet)
+        *packet = (buffer_size ? g_memdup (&tlv_data[offset], buffer_size) : NULL);
+
+    return TRUE;
+}
diff --git a/src/libmbim-glib/mbim-tlv.h b/src/libmbim-glib/mbim-tlv.h
new file mode 100644
index 0000000..64b9c8b
--- /dev/null
+++ b/src/libmbim-glib/mbim-tlv.h
@@ -0,0 +1,315 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Intel Corporation
+ */
+
+#ifndef _LIBMBIM_GLIB_MBIM_TLV_H_
+#define _LIBMBIM_GLIB_MBIM_TLV_H_
+
+#if !defined (__LIBMBIM_GLIB_H_INSIDE__) && !defined (LIBMBIM_GLIB_COMPILATION)
+#error "Only <libmbim-glib.h> can be included directly."
+#endif
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "mbim-uuid.h"
+
+G_BEGIN_DECLS
+
+/**
+ * SECTION:mbim-tlv
+ * @title: MbimTlv
+ * @short_description: A variable-sized data structure specified in Microsoft
+ *   MBIM extension v3.0.
+ *
+ * The #MbimTlv structure is capable of exchanging a wide range of information
+ * between an MBIM host and an MBIM device.
+ *
+ * CID payloads for requests, responses, and/or notifications may contain zero
+ * or more unnamed and optional Information Elements (IE) encoded as #MbimTlv
+ * fields.
+ */
+
+/**
+ * MbimTlv:
+ *
+ * An opaque type representing a MBIM TLV.
+ *
+ * Since: 1.28
+ */
+typedef struct _MbimTlv MbimTlv;
+
+GType mbim_tlv_get_type (void) G_GNUC_CONST;
+#define MBIM_TYPE_TLV (mbim_tlv_get_type ())
+
+/*****************************************************************************/
+/* Generic TLV interface */
+
+/**
+ * MbimTlvType:
+ * @MBIM_TLV_TYPE_INVALID: Invalid TLV type.
+ * @MBIM_TLV_TYPE_UE_POLICITES: UE policies.
+ * @MBIM_TLV_TYPE_SINGLE_NSSAI: Single NSSAI.
+ * @MBIM_TLV_TYPE_ALLOWED_NSSAI: Allowed NSSAI.
+ * @MBIM_TLV_TYPE_CFG_NSSAI: Configured NSSAI.
+ * @MBIM_TLV_TYPE_DFLT_CFG_NSSAI: Default configured NSSAI.
+ * @MBIM_TLV_TYPE_PRECFG_DFLT_CFG_NSSAI: Preconfigured default configured NSSAI.
+ * @MBIM_TLV_TYPE_REJ_NSSAI: Rejected NSSAI.
+ * @MBIM_TLV_TYPE_LADN: Local Area Data Network (LADN).
+ * @MBIM_TLV_TYPE_TAI: Tracking Area Identity (TAI).
+ * @MBIM_TLV_TYPE_WCHAR_STR: WCHAR string.
+ * @MBIM_TLV_TYPE_UINT16_TBL: Array of 1 or more @guint16 entries.
+ * @MBIM_TLV_TYPE_EAP_PACKET: Extensible Authentication Protocol packet.
+ * @MBIM_TLV_TYPE_PCO: Protocol Configuration Option (PCO).
+ * @MBIM_TLV_TYPE_ROUTE_SELECTION_DESCRIPTORS: One or more route selection descriptors.
+ * @MBIM_TLV_TYPE_TRAFFIC_PARAMETERS: A traffic parameters record.
+ * @MBIM_TLV_TYPE_WAKE_COMMAND: Wake command.
+ * @MBIM_TLV_TYPE_WAKE_PACKET: Wake packet.
+ *
+ * Type of the MBIM TLV.
+ *
+ * Since: 1.28
+ */
+typedef enum {
+    MBIM_TLV_TYPE_INVALID                     = 0,
+    MBIM_TLV_TYPE_UE_POLICITES                = 1,
+    MBIM_TLV_TYPE_SINGLE_NSSAI                = 2,
+    MBIM_TLV_TYPE_ALLOWED_NSSAI               = 3,
+    MBIM_TLV_TYPE_CFG_NSSAI                   = 4,
+    MBIM_TLV_TYPE_DFLT_CFG_NSSAI              = 5,
+    MBIM_TLV_TYPE_PRECFG_DFLT_CFG_NSSAI       = 6,
+    MBIM_TLV_TYPE_REJ_NSSAI                   = 7,
+    MBIM_TLV_TYPE_LADN                        = 8,
+    MBIM_TLV_TYPE_TAI                         = 9,
+    MBIM_TLV_TYPE_WCHAR_STR                   = 10,
+    MBIM_TLV_TYPE_UINT16_TBL                  = 11,
+    MBIM_TLV_TYPE_EAP_PACKET                  = 12,
+    MBIM_TLV_TYPE_PCO                         = 13,
+    MBIM_TLV_TYPE_ROUTE_SELECTION_DESCRIPTORS = 14,
+    MBIM_TLV_TYPE_TRAFFIC_PARAMETERS          = 15,
+    MBIM_TLV_TYPE_WAKE_COMMAND                = 16,
+    MBIM_TLV_TYPE_WAKE_PACKET                 = 17,
+} MbimTlvType;
+
+/**
+ * mbim_tlv_new:
+ * @type: a #MbimTlvType.
+ * @data: contents of the TLV.
+ * @data_length: length of the message.
+ *
+ * Create a #MbimTlv with the given contents.
+ *
+ * Returns: (transfer full): a newly created #MbimTlv, which should be freed with mbim_tlv_unref().
+ *
+ * Since: 1.28
+ */
+MbimTlv *mbim_tlv_new (MbimTlvType   type,
+                       const guint8 *data,
+                       guint32       data_length);
+
+/**
+ * mbim_tlv_dup:
+ * @self: a #MbimTlv to duplicate.
+ *
+ * Create a #MbimTlv with the same contents as @self.
+ *
+ * Returns: (transfer full): a newly created #MbimTlv, which should be freed with mbim_tlv_unref().
+ *
+ * Since: 1.28
+ */
+MbimTlv *mbim_tlv_dup (const MbimTlv *self);
+
+/**
+ * mbim_tlv_ref:
+ * @self: a #MbimTlv.
+ *
+ * Atomically increments the reference count of @self by one.
+ *
+ * Returns: (transfer full): the new reference to @self.
+ *
+ * Since: 1.28
+ */
+MbimTlv *mbim_tlv_ref (MbimTlv *self);
+
+/**
+ * mbim_tlv_unref:
+ * @self: a #MbimTlv.
+ *
+ * Atomically decrements the reference count of @self by one.
+ * If the reference count drops to 0, @self is completely disposed.
+ *
+ * Since: 1.28
+ */
+void mbim_tlv_unref (MbimTlv *self);
+
+/**
+ * mbim_tlv_get_raw:
+ * @self: a #MbimTlv.
+ * @length: (out): return location for the size of the output buffer.
+ * @error: return location for error or %NULL.
+ *
+ * Gets the whole raw data buffer of the #MbimTlv.
+ *
+ * Returns: The raw data buffer, or #NULL if @error is set.
+ *
+ * Since: 1.28
+ */
+const guint8 *mbim_tlv_get_raw (const MbimTlv  *self,
+                                guint32        *length,
+                                GError        **error);
+
+/**
+ * mbim_tlv_get_tlv_type:
+ * @self: a #MbimTlv.
+ *
+ * Gets the message type.
+ *
+ * Returns: a #MbimTlvType.
+ *
+ * Since: 1.28
+ */
+MbimTlvType mbim_tlv_get_tlv_type (const MbimTlv *self);
+
+/**
+ * mbim_tlv_get_tlv_data:
+ * @self: a #MbimTlv.
+ * @out_length: (out): return location for the size of the output buffer.
+ *
+ * Gets the TLV raw data.
+ *
+ * Returns: (transfer none): The raw data buffer, or #NULL if empty.
+ *
+ * Since: 1.28
+ */
+const guint8 *mbim_tlv_get_tlv_data (const MbimTlv *self,
+                                     guint32       *out_length);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MbimTlv, mbim_tlv_unref)
+
+/*****************************************************************************/
+/* String TLV type helpers */
+
+/**
+ * mbim_tlv_string_new:
+ * @str: a string.
+ * @error: return location for error or %NULL.
+ *
+ * Create a #MbimTlv of type %MBIM_TLV_TYPE_WCHAR_STR with the given contents.
+ *
+ * Returns: (transfer full): a newly created #MbimTlv which should be freed with mbim_tlv_unref(), or %NULL if @error is set.
+ *
+ * Since: 1.28
+ */
+MbimTlv *mbim_tlv_string_new (const gchar  *str,
+                              GError      **error);
+
+/**
+ * mbim_tlv_string_get:
+ * @self: a #MbimTlv of type %MBIM_TLV_TYPE_WCHAR_STR.
+ * @error: return location for error or %NULL.
+ *
+ * Get a string with the contents in the #MbimTlv.
+ *
+ * Returns: (transfer full): a newly created string, which should be freed with g_free(), or %NULL if @error is set.
+ *
+ * Since: 1.28
+ */
+gchar *mbim_tlv_string_get (const MbimTlv  *self,
+                            GError        **error);
+
+/*****************************************************************************/
+/* guint16 array type helpers */
+
+/**
+ * mbim_tlv_guint16_array_get:
+ * @self: a #MbimTlv of type %MBIM_TLV_TYPE_UINT16_TBL.
+ * @array_size: (out)(optional)(transfer none): return location for a #guint32,
+ *  or %NULL if the field is not needed.
+ * @array: (out)(optional)(transfer full)(type guint16): return location for a
+ *  newly allocated array of #guint16 values, or %NULL if the field is not
+ *  needed. Free the returned value with g_free().
+ * @error: return location for error or %NULL.
+ *
+ * Get an array of #guint16 values with the contents in the #MbimTlv.
+ *
+ * The method may return a successful return even with on empty arrays (i.e.
+ * with @array_size set to 0 and @array set to %NULL).
+ *
+ * Returns: %TRUE if on success, %FALSE if @error is set.
+ *
+ * Since: 1.28
+ */
+gboolean mbim_tlv_guint16_array_get (const MbimTlv  *self,
+                                     guint32        *array_size,
+                                     guint16       **array,
+                                     GError        **error);
+
+/*****************************************************************************/
+/* wake command type helpers */
+
+/**
+ * mbim_tlv_wake_command_get:
+ * @self: a #MbimTlv of type %MBIM_TLV_TYPE_WAKE_COMMAND.
+ * @service: (out)(optional)(transfer none): return location for a #MbimUuid
+ *   specifying the service that triggered the wake.
+ * @cid: (out)(optional)(transfer none): return location for the command id that
+ *   triggered the wake.
+ * @payload_size: (out)(optional)(transfer none): return location for a #guint32,
+ *  or %NULL if the field is not needed.
+ * @payload: (out)(optional)(transfer full)(type guint8): return location for a
+ *  newly allocated array of #guint8 values, or %NULL if the field is not
+ *  needed. Free the returned value with g_free().
+ *
+ * Get the contents of a wake command TLV.
+ *
+ * The method may return a successful return even with on empty payload (i.e.
+ * with @payload_size set to 0 and @payload set to %NULL).
+ *
+ * Returns: %TRUE if on success, %FALSE if @error is set.
+ *
+ * Since: 1.28
+ */
+gboolean mbim_tlv_wake_command_get (const MbimTlv   *self,
+                                    const MbimUuid **service,
+                                    guint32         *cid,
+                                    guint32         *payload_size,
+                                    guint8         **payload,
+                                    GError         **error);
+
+/*****************************************************************************/
+/* wake packet type helpers */
+
+/**
+ * mbim_tlv_wake_packet_get:
+ * @self: a #MbimTlv of type %MBIM_TLV_TYPE_WAKE_PACKET.
+ * @filter_id: (out)(optional)(transfer none): return location for a #guint32
+ *   specifying the filter id.
+ * @original_packet_size: (out)(optional)(transfer none): return location for a
+ *  #guint32, or %NULL if the field is not needed.
+ * @packet_size: (out)(optional)(transfer none): return location for a #guint32,
+ *  or %NULL if the field is not needed.
+ * @packet: (out)(optional)(transfer full)(type guint8): return location for a
+ *  newly allocated array of #guint8 values, or %NULL if the field is not
+ *  needed. Free the returned value with g_free().
+ *
+ * Get the contents of a wake packet TLV.
+ *
+ * Returns: %TRUE if on success, %FALSE if @error is set.
+ *
+ * Since: 1.28
+ */
+gboolean mbim_tlv_wake_packet_get (const MbimTlv  *self,
+                                   guint32        *filter_id,
+                                   guint32        *original_packet_size,
+                                   guint32        *packet_size,
+                                   guint8        **packet,
+                                   GError        **error);
+
+G_END_DECLS
+
+#endif /* _LIBMBIM_GLIB_MBIM_TLV_H_ */
diff --git a/src/libmbim-glib/mbim-uuid.c b/src/libmbim-glib/mbim-uuid.c
index dcadb27..4e90a37 100644
--- a/src/libmbim-glib/mbim-uuid.c
+++ b/src/libmbim-glib/mbim-uuid.c
@@ -510,6 +510,46 @@
     .e = { 0x03, 0x3C, 0x39, 0xF6, 0x0D, 0xB9 }
 };
 
+static const MbimUuid uuid_context_type_admin = {
+    .a = { 0x5F, 0x7E, 0x4C, 0x2E },
+    .b = { 0xE8, 0x0B },
+    .c = { 0x40, 0xA9 },
+    .d = { 0xA2, 0x39 },
+    .e = { 0xF0, 0xAB, 0xCF, 0xD1, 0x1F, 0x4B }
+};
+
+static const MbimUuid uuid_context_type_app = {
+    .a = { 0x74, 0xD8, 0x8A, 0x3D },
+    .b = { 0xDF, 0xBD },
+    .c = { 0x47, 0x99 },
+    .d = { 0x9A, 0x8C },
+    .e = { 0x73, 0x10, 0xA3, 0x7B, 0xB2, 0xEE }
+};
+
+static const MbimUuid uuid_context_type_xcap = {
+    .a = { 0x50, 0xD3, 0x78, 0xA7 },
+    .b = { 0xBA, 0xA5 },
+    .c = { 0x4A, 0x50 },
+    .d = { 0xB8, 0x72 },
+    .e = { 0x3F, 0xE5, 0xBB, 0x46, 0x34, 0x11 }
+};
+
+static const MbimUuid uuid_context_type_tethering = {
+    .a = { 0x5E, 0x4E, 0x06, 0x01 },
+    .b = { 0x48, 0xDC },
+    .c = { 0x4E, 0x2B },
+    .d = { 0xAC, 0xB8 },
+    .e = { 0x08, 0xB4, 0x01, 0x6B, 0xBA, 0xAC }
+};
+
+static const MbimUuid uuid_context_type_emergency_calling = {
+    .a = { 0x5F, 0x41, 0xAD, 0xB8 },
+    .b = { 0x20, 0x4E },
+    .c = { 0x4D, 0x31 },
+    .d = { 0x9D, 0xA8 },
+    .e = { 0xB3, 0xC9, 0x70, 0xE3, 0x60, 0xF2 }
+};
+
 const MbimUuid *
 mbim_uuid_from_context_type (MbimContextType context_type)
 {
@@ -536,6 +576,16 @@
         return &uuid_context_type_mms;
     case MBIM_CONTEXT_TYPE_LOCAL:
         return &uuid_context_type_local;
+    case MBIM_CONTEXT_TYPE_ADMIN:
+        return &uuid_context_type_admin;
+    case MBIM_CONTEXT_TYPE_APP:
+        return &uuid_context_type_app;
+    case MBIM_CONTEXT_TYPE_XCAP:
+        return &uuid_context_type_xcap;
+    case MBIM_CONTEXT_TYPE_TETHERING:
+        return &uuid_context_type_tethering;
+    case MBIM_CONTEXT_TYPE_EMERGENCY_CALLING:
+        return &uuid_context_type_emergency_calling;
     default:
         g_assert_not_reached ();
     }
@@ -571,5 +621,20 @@
     if (mbim_uuid_cmp (uuid, &uuid_context_type_local))
         return MBIM_CONTEXT_TYPE_LOCAL;
 
+    if (mbim_uuid_cmp (uuid, &uuid_context_type_admin))
+        return MBIM_CONTEXT_TYPE_ADMIN;
+
+    if (mbim_uuid_cmp (uuid, &uuid_context_type_app))
+        return MBIM_CONTEXT_TYPE_APP;
+
+    if (mbim_uuid_cmp (uuid, &uuid_context_type_xcap))
+        return MBIM_CONTEXT_TYPE_XCAP;
+
+    if (mbim_uuid_cmp (uuid, &uuid_context_type_tethering))
+        return MBIM_CONTEXT_TYPE_TETHERING;
+
+    if (mbim_uuid_cmp (uuid, &uuid_context_type_emergency_calling))
+        return MBIM_CONTEXT_TYPE_EMERGENCY_CALLING;
+
     return MBIM_CONTEXT_TYPE_INVALID;
 }
diff --git a/src/libmbim-glib/mbim-uuid.h b/src/libmbim-glib/mbim-uuid.h
index 0ffed7c..fa62d86 100644
--- a/src/libmbim-glib/mbim-uuid.h
+++ b/src/libmbim-glib/mbim-uuid.h
@@ -328,6 +328,17 @@
 #define MBIM_UUID_MS_BASIC_CONNECT_EXTENSIONS mbim_uuid_from_service (MBIM_SERVICE_MS_BASIC_CONNECT_EXTENSIONS)
 
 /**
+ * MBIM_UUID_MS_UICC_LOW_LEVEL_ACCESS:
+ *
+ * Get the UUID of the %MBIM_SERVICE_MS_UICC_LOW_LEVEL_ACCESS service.
+ *
+ * Returns: (transfer none): a #MbimUuid.
+ *
+ * Since: 1.26
+ */
+#define MBIM_UUID_MS_UICC_LOW_LEVEL_ACCESS mbim_uuid_from_service (MBIM_SERVICE_MS_UICC_LOW_LEVEL_ACCESS)
+
+/**
  * mbim_service_lookup_name:
  * @service: a MbimService or custom service.
  *
@@ -413,30 +424,50 @@
  * MbimContextType:
  * @MBIM_CONTEXT_TYPE_INVALID: Invalid context type.
  * @MBIM_CONTEXT_TYPE_NONE: Context not yet provisioned.
- * @MBIM_CONTEXT_TYPE_INTERNET: Connection to the Internet.
- * @MBIM_CONTEXT_TYPE_VPN: Connection to a VPN.
- * @MBIM_CONTEXT_TYPE_VOICE: Connection to a VoIP service.
- * @MBIM_CONTEXT_TYPE_VIDEO_SHARE: Connection to a video sharing service.
- * @MBIM_CONTEXT_TYPE_PURCHASE: Connection to an over-the-air activation site.
- * @MBIM_CONTEXT_TYPE_IMS: Connection to IMS.
- * @MBIM_CONTEXT_TYPE_MMS: Connection to MMS.
- * @MBIM_CONTEXT_TYPE_LOCAL: A local.
+ * @MBIM_CONTEXT_TYPE_INTERNET: Context representing a connection to the
+ *  Internet.
+ * @MBIM_CONTEXT_TYPE_VPN: Context representing a connection to a a private
+ *  network (e.g. VPN to a corporate network).
+ * @MBIM_CONTEXT_TYPE_VOICE: Context representing a connection to a VoIP service.
+ * @MBIM_CONTEXT_TYPE_VIDEO_SHARE: Context representing a connection to a video
+ *  sharing service.
+ * @MBIM_CONTEXT_TYPE_PURCHASE: Context representing a connection to an
+ *  OTA (over-the-air) activation site.
+ * @MBIM_CONTEXT_TYPE_IMS: Context representing a connection to IMS.
+ * @MBIM_CONTEXT_TYPE_MMS: Context representing a connection to MMS.
+ * @MBIM_CONTEXT_TYPE_LOCAL: Context representing a connection which is
+ *  terminated at the device. No IP traffic sent over the air.
+ * @MBIM_CONTEXT_TYPE_ADMIN: Context used for administrative purposes, such as
+ *  device management (MS MBIMEx). Since 1.28.
+ * @MBIM_CONTEXT_TYPE_APP: Context used for certain applications allowed by
+ *  mobile operators (MS MBIMEx). Since 1.28.
+ * @MBIM_CONTEXT_TYPE_XCAP: Context used for XCAP provisioning on IMS services
+ *  (MS MBIMEx). Since 1.28.
+ * @MBIM_CONTEXT_TYPE_TETHERING: Context used for mobile hotspot tethering
+ *  (MS MBIMEx). Since 1.28.
+ * @MBIM_CONTEXT_TYPE_EMERGENCY_CALLING: Context used for IMS emergency calling
+ *  (MS MBIMEx). Since 1.28.
  *
  * Enumeration of the generic MBIM context types.
  *
  * Since: 1.0
  */
 typedef enum { /*< since=1.0 >*/
-    MBIM_CONTEXT_TYPE_INVALID     = 0,
-    MBIM_CONTEXT_TYPE_NONE        = 1,
-    MBIM_CONTEXT_TYPE_INTERNET    = 2,
-    MBIM_CONTEXT_TYPE_VPN         = 3,
-    MBIM_CONTEXT_TYPE_VOICE       = 4,
-    MBIM_CONTEXT_TYPE_VIDEO_SHARE = 5,
-    MBIM_CONTEXT_TYPE_PURCHASE    = 6,
-    MBIM_CONTEXT_TYPE_IMS         = 7,
-    MBIM_CONTEXT_TYPE_MMS         = 8,
-    MBIM_CONTEXT_TYPE_LOCAL       = 9,
+    MBIM_CONTEXT_TYPE_INVALID           = 0,
+    MBIM_CONTEXT_TYPE_NONE              = 1,
+    MBIM_CONTEXT_TYPE_INTERNET          = 2,
+    MBIM_CONTEXT_TYPE_VPN               = 3,
+    MBIM_CONTEXT_TYPE_VOICE             = 4,
+    MBIM_CONTEXT_TYPE_VIDEO_SHARE       = 5,
+    MBIM_CONTEXT_TYPE_PURCHASE          = 6,
+    MBIM_CONTEXT_TYPE_IMS               = 7,
+    MBIM_CONTEXT_TYPE_MMS               = 8,
+    MBIM_CONTEXT_TYPE_LOCAL             = 9,
+    MBIM_CONTEXT_TYPE_ADMIN             = 10,
+    MBIM_CONTEXT_TYPE_APP               = 11,
+    MBIM_CONTEXT_TYPE_XCAP              = 12,
+    MBIM_CONTEXT_TYPE_TETHERING         = 13,
+    MBIM_CONTEXT_TYPE_EMERGENCY_CALLING = 14,
 } MbimContextType;
 
 /**
@@ -463,15 +494,6 @@
  */
 MbimContextType mbim_uuid_to_context_type (const MbimUuid *uuid);
 
-/**
- * MBIM_UUID_MS_UICC_LOW_LEVEL_ACCESS:
- *
- * Get the UUID of the %MBIM_SERVICE_MS_UICC_LOW_LEVEL_ACCESS service.
- *
- * Returns: (transfer none): a #MbimUuid.
- */
-#define MBIM_UUID_MS_UICC_LOW_LEVEL_ACCESS mbim_uuid_from_service (MBIM_SERVICE_MS_UICC_LOW_LEVEL_ACCESS)
-
 G_END_DECLS
 
 #endif /* _LIBMBIM_GLIB_MBIM_UUID_H_ */
diff --git a/src/libmbim-glib/meson.build b/src/libmbim-glib/meson.build
index 1202ca2..7574f5b 100644
--- a/src/libmbim-glib/meson.build
+++ b/src/libmbim-glib/meson.build
@@ -10,6 +10,7 @@
   'mbim-enums.h',
   'mbim-message.h',
   'mbim-uuid.h',
+  'mbim-tlv.h',
 )
 
 subdir('generated')
@@ -24,13 +25,13 @@
 
 install_headers(
   headers,
-  subdir: mbim_glib_include_subdir,
+  install_dir: mbim_glib_pkgincludedir,
 )
 
 common_c_flags = [
   '-DLIBMBIM_GLIB_COMPILATION',
   '-DG_LOG_DOMAIN="Mbim"',
-  '-DLIBEXEC_PATH="@0@"'.format(join_paths(mbim_prefix, mbim_libexecdir)),
+  '-DLIBEXEC_PATH="@0@"'.format(mbim_prefix / mbim_libexecdir),
 ]
 
 sources = files(
@@ -44,6 +45,7 @@
   'mbim-proxy-helpers.c',
   'mbim-utils.c',
   'mbim-uuid.c',
+  'mbim-tlv.c',
 )
 
 deps = [
@@ -75,7 +77,8 @@
   install_dir: mbim_glib_pkgincludedir,
 )
 
-symbol_map = join_paths(meson.current_source_dir(), 'symbol.map')
+symbol_map = meson.current_source_dir() / 'symbol.map'
+ldflags = cc.get_supported_link_arguments('-Wl,--version-script,@0@'.format(symbol_map))
 
 libmbim_glib = library(
   libname,
@@ -83,7 +86,7 @@
   sources: version_header,
   include_directories: top_inc,
   dependencies: generated_dep,
-  link_args: '-Wl,--version-script,' + symbol_map,
+  link_args: ldflags,
   link_depends: symbol_map,
   link_whole: libmbim_glib_core,
   install: true,
diff --git a/src/libmbim-glib/test/meson.build b/src/libmbim-glib/test/meson.build
index 3c33bdb..a501cfe 100644
--- a/src/libmbim-glib/test/meson.build
+++ b/src/libmbim-glib/test/meson.build
@@ -11,14 +11,12 @@
   'proxy-helpers',
 ]
 
-random_number = mbim_minor_version + meson.version().split('.').get(1).to_int()
-
-test_env = environment()
-test_env.set('G_DEBUG', 'gc-friendly')
-test_env.set('MALLOC_CHECK_', '2')
-test_env.set('MALLOC_PERTURB_', (random_number % 256).to_string())
-test_env.set('G_TEST_SRCDIR', meson.current_source_dir())
-test_env.set('G_TEST_BUILDDIR', meson.current_build_dir())
+test_env = {
+  'G_DEBUG': 'gc-friendly',
+  'MALLOC_CHECK_': '2',
+  'G_TEST_SRCDIR': meson.current_source_dir(),
+  'G_TEST_BUILDDIR': meson.current_build_dir(),
+}
 
 foreach test_unit: test_units
   test_name = 'test-' + test_unit
diff --git a/src/libmbim-glib/test/test-message-builder.c b/src/libmbim-glib/test/test-message-builder.c
index 45442d7..df50bc1 100644
--- a/src/libmbim-glib/test/test-message-builder.c
+++ b/src/libmbim-glib/test/test-message-builder.c
@@ -18,8 +18,8 @@
 #include "mbim-stk.h"
 #include "mbim-dss.h"
 #include "mbim-ms-host-shutdown.h"
+#include "mbim-ms-basic-connect-extensions.h"
 
-#if defined ENABLE_TEST_MESSAGE_TRACES
 static void
 test_message_trace (const guint8 *computed,
                     guint32       computed_size,
@@ -55,12 +55,28 @@
     g_free (message_str);
     g_free (expected_str);
 }
-#else
-#define test_message_trace(...)
-#endif
 
 static void
-test_message_builder_basic_connect_pin_set_raw (void)
+test_message_printable (MbimMessage *message,
+                        guint8       mbimex_version_major,
+                        guint8       mbimex_version_minor)
+{
+    g_autofree gchar *printable = NULL;
+
+    printable = mbim_message_get_printable_full (message,
+                                                 mbimex_version_major,
+                                                 mbimex_version_minor,
+                                                 "---- ",
+                                                 FALSE,
+                                                 NULL);
+    g_print ("\n"
+             "Message printable:\n"
+             "%s\n",
+             printable);
+}
+
+static void
+test_basic_connect_pin_set_raw (void)
 {
     MbimMessage *message;
     MbimMessageCommandBuilder *builder;
@@ -120,11 +136,13 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
     mbim_message_unref (message);
 }
 
 static void
-test_message_builder_basic_connect_pin_set (void)
+test_basic_connect_pin_set (void)
 {
     GError *error = NULL;
     MbimMessage *message;
@@ -181,11 +199,13 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
     mbim_message_unref (message);
 }
 
 static void
-test_message_builder_basic_connect_connect_set_raw (void)
+test_basic_connect_connect_set_raw (void)
 {
     MbimMessage *message;
     MbimMessageCommandBuilder *builder;
@@ -262,11 +282,13 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
     mbim_message_unref (message);
 }
 
 static void
-test_message_builder_basic_connect_connect_set (void)
+test_basic_connect_connect_set (void)
 {
     GError *error = NULL;
     MbimMessage *message;
@@ -341,11 +363,13 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
     mbim_message_unref (message);
 }
 
 static void
-test_message_builder_basic_connect_service_activation_set (void)
+test_basic_connect_service_activation_set (void)
 {
     GError *error = NULL;
     MbimMessage *message;
@@ -398,11 +422,13 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
     mbim_message_unref (message);
 }
 
 static void
-test_message_builder_basic_connect_device_service_subscribe_list_set (void)
+test_basic_connect_device_service_subscribe_list_set (void)
 {
     GError *error = NULL;
     MbimEventEntry **entries;
@@ -488,11 +514,13 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
     mbim_message_unref (message);
 }
 
 static void
-test_message_builder_ussd_set (void)
+test_ussd_set (void)
 {
     GError *error = NULL;
     MbimMessage *message;
@@ -558,11 +586,13 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
     mbim_message_unref (message);
 }
 
 static void
-test_message_builder_auth_akap_query (void)
+test_auth_akap_query (void)
 {
     GError *error = NULL;
     MbimMessage *message;
@@ -638,11 +668,13 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
     mbim_message_unref (message);
 }
 
 static void
-test_message_builder_stk_pac_set (void)
+test_stk_pac_set (void)
 {
     GError *error = NULL;
     MbimMessage *message;
@@ -709,11 +741,13 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
     mbim_message_unref (message);
 }
 
 static void
-test_message_builder_stk_terminal_response_set (void)
+test_stk_terminal_response_set (void)
 {
     GError *error = NULL;
     MbimMessage *message;
@@ -780,11 +814,13 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
     mbim_message_unref (message);
 }
 
 static void
-test_message_builder_stk_envelope_set (void)
+test_stk_envelope_set (void)
 {
     GError *error = NULL;
     MbimMessage *message;
@@ -856,11 +892,13 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
     mbim_message_unref (message);
 }
 
 static void
-test_message_builder_basic_connect_ip_packet_filters_set_none (void)
+test_basic_connect_ip_packet_filters_set_none (void)
 {
     GError *error = NULL;
     MbimMessage *message;
@@ -908,11 +946,13 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
     mbim_message_unref (message);
 }
 
 static void
-test_message_builder_basic_connect_ip_packet_filters_set_one (void)
+test_basic_connect_ip_packet_filters_set_one (void)
 {
     GError *error = NULL;
     MbimMessage *message;
@@ -993,13 +1033,15 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
     mbim_message_unref (message);
 
     mbim_packet_filter_array_free (filters);
 }
 
 static void
-test_message_builder_basic_connect_ip_packet_filters_set_two (void)
+test_basic_connect_ip_packet_filters_set_two (void)
 {
     GError *error = NULL;
     MbimMessage *message;
@@ -1107,13 +1149,15 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
     mbim_message_unref (message);
 
     mbim_packet_filter_array_free (filters);
 }
 
 static void
-test_message_builder_dss_connect_set (void)
+test_dss_connect_set (void)
 {
     GError *error = NULL;
     MbimMessage *message;
@@ -1177,11 +1221,13 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
     mbim_message_unref (message);
 }
 
 static void
-test_message_builder_basic_connect_multicarrier_providers_set (void)
+test_basic_connect_multicarrier_providers_set (void)
 {
     GError *error = NULL;
     MbimMessage *message;
@@ -1284,12 +1330,14 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
     mbim_message_unref (message);
     mbim_provider_array_free (providers);
 }
 
 static void
-test_message_builder_ms_host_shutdown_notify_set (void)
+test_ms_host_shutdown_notify_set (void)
 {
     GError *error = NULL;
     MbimMessage *message;
@@ -1334,6 +1382,332 @@
     g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
     g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
 
+    test_message_printable (message, 1, 0);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_ms_basic_connect_extensions_registration_parameters_set_0_unnamed_tlvs (void)
+{
+    g_autoptr(GError)      error = NULL;
+    g_autoptr(MbimMessage) message = NULL;
+    const guint8           expected_message [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x44, 0x00, 0x00, 0x00, /* length */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0x3D, 0x01, 0xDC, 0xC5, /* service id */
+        0xFE, 0xF5, 0x4D, 0x05,
+        0x0D, 0x3A, 0xBE, 0xF7,
+        0x05, 0x8E, 0x9A, 0xAF,
+        0x11, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x14, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x00, 0x00, 0x00, 0x00, /* mico mode */
+        0x00, 0x00, 0x00, 0x00, /* drx cycle */
+        0x00, 0x00, 0x00, 0x00, /* ladn info */
+        0x01, 0x00, 0x00, 0x00, /* pdu hint */
+        0x01, 0x00, 0x00, 0x00, /* re register if needed */
+        /* no unnamed TLVs */
+    };
+
+    message = (mbim_message_ms_basic_connect_extensions_v3_registration_parameters_set_new (
+                   MBIM_MICO_MODE_DISABLED,
+                   MBIM_DRX_CYCLE_NOT_SPECIFIED,
+                   MBIM_LADN_INFO_NOT_NEEDED,
+                   MBIM_DEFAULT_PDU_ACTIVATION_HINT_LIKELY,
+                   TRUE,
+                   NULL, /* 0 unnamed ies */
+                   &error));
+    g_assert_no_error (error);
+    g_assert (message != NULL);
+
+    mbim_message_set_transaction_id (message, 1);
+
+    test_message_trace ((const guint8 *)((GByteArray *)message)->data,
+                        ((GByteArray *)message)->len,
+                        expected_message,
+                        sizeof (expected_message));
+
+    g_assert_cmpuint (mbim_message_get_transaction_id (message), ==, 1);
+    g_assert_cmpuint (mbim_message_get_message_type   (message), ==, MBIM_MESSAGE_TYPE_COMMAND);
+    g_assert_cmpuint (mbim_message_get_message_length (message), ==, sizeof (expected_message));
+
+    g_assert_cmpuint (mbim_message_command_get_service      (message), ==, MBIM_SERVICE_MS_BASIC_CONNECT_EXTENSIONS);
+    g_assert_cmpuint (mbim_message_command_get_cid          (message), ==, MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_REGISTRATION_PARAMETERS);
+    g_assert_cmpuint (mbim_message_command_get_command_type (message), ==, MBIM_MESSAGE_COMMAND_TYPE_SET);
+
+    g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
+    g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
+
+    test_message_printable (message, 3, 0);
+}
+
+static void
+test_ms_basic_connect_extensions_registration_parameters_set_1_unnamed_tlv (void)
+{
+    MbimTlv                *tlv;
+    GList                  *tlv_list = NULL;
+    g_autoptr(GError)       error = NULL;
+    g_autoptr(MbimMessage)  message = NULL;
+    const guint8            expected_message [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x58, 0x00, 0x00, 0x00, /* length */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0x3D, 0x01, 0xDC, 0xC5, /* service id */
+        0xFE, 0xF5, 0x4D, 0x05,
+        0x0D, 0x3A, 0xBE, 0xF7,
+        0x05, 0x8E, 0x9A, 0xAF,
+        0x11, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x28, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x00, 0x00, 0x00, 0x00, /* mico mode */
+        0x00, 0x00, 0x00, 0x00, /* drx cycle */
+        0x00, 0x00, 0x00, 0x00, /* ladn info */
+        0x01, 0x00, 0x00, 0x00, /* pdu hint */
+        0x01, 0x00, 0x00, 0x00, /* re register if needed */
+        /* First unnamed TLV */
+        0x0A, 0x00, 0x00, 0x00, /* TLV type MBIM_TLV_TYPE_WCHAR_STR, no padding */
+        0x0C, 0x00, 0x00, 0x00, /* TLV data length */
+        0x4F, 0x00, 0x72, 0x00, /* TLV data string */
+        0x61, 0x00, 0x6E, 0x00,
+        0x67, 0x00, 0x65, 0x00,
+    };
+
+    tlv = mbim_tlv_string_new ("Orange", &error);
+    g_assert_no_error (error);
+    g_assert (tlv);
+    tlv_list = g_list_append (tlv_list, tlv);
+
+    message = (mbim_message_ms_basic_connect_extensions_v3_registration_parameters_set_new (
+                   MBIM_MICO_MODE_DISABLED,
+                   MBIM_DRX_CYCLE_NOT_SPECIFIED,
+                   MBIM_LADN_INFO_NOT_NEEDED,
+                   MBIM_DEFAULT_PDU_ACTIVATION_HINT_LIKELY,
+                   TRUE,
+                   tlv_list,
+                   &error));
+    g_assert_no_error (error);
+    g_assert (message != NULL);
+
+    mbim_message_set_transaction_id (message, 1);
+
+    test_message_trace ((const guint8 *)((GByteArray *)message)->data,
+                        ((GByteArray *)message)->len,
+                        expected_message,
+                        sizeof (expected_message));
+
+    g_assert_cmpuint (mbim_message_get_transaction_id (message), ==, 1);
+    g_assert_cmpuint (mbim_message_get_message_type   (message), ==, MBIM_MESSAGE_TYPE_COMMAND);
+    g_assert_cmpuint (mbim_message_get_message_length (message), ==, sizeof (expected_message));
+
+    g_assert_cmpuint (mbim_message_command_get_service      (message), ==, MBIM_SERVICE_MS_BASIC_CONNECT_EXTENSIONS);
+    g_assert_cmpuint (mbim_message_command_get_cid          (message), ==, MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_REGISTRATION_PARAMETERS);
+    g_assert_cmpuint (mbim_message_command_get_command_type (message), ==, MBIM_MESSAGE_COMMAND_TYPE_SET);
+
+    g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
+    g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
+
+    g_list_free_full (tlv_list, (GDestroyNotify)mbim_tlv_unref);
+
+    test_message_printable (message, 3, 0);
+}
+
+static void
+test_ms_basic_connect_extensions_registration_parameters_set_3_unnamed_tlvs (void)
+{
+    MbimTlv                *tlv;
+    GList                  *tlv_list = NULL;
+    g_autoptr(GError)       error = NULL;
+    g_autoptr(MbimMessage)  message = NULL;
+    const guint8            pco[] = {
+        0x01, 0x02, 0x03, 0x04,
+        0x05, 0x06, 0x07, 0x08,
+        0x09, 0x0A, 0x0B };
+    const guint8            expected_message [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x80, 0x00, 0x00, 0x00, /* length */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0x3D, 0x01, 0xDC, 0xC5, /* service id */
+        0xFE, 0xF5, 0x4D, 0x05,
+        0x0D, 0x3A, 0xBE, 0xF7,
+        0x05, 0x8E, 0x9A, 0xAF,
+        0x11, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x50, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x00, 0x00, 0x00, 0x00, /* mico mode */
+        0x00, 0x00, 0x00, 0x00, /* drx cycle */
+        0x00, 0x00, 0x00, 0x00, /* ladn info */
+        0x01, 0x00, 0x00, 0x00, /* pdu hint */
+        0x01, 0x00, 0x00, 0x00, /* re register if needed */
+        /* First unnamed TLV */
+        0x0A, 0x00, 0x00, 0x02, /* TLV type MBIM_TLV_TYPE_WCHAR_STR, padding 2 */
+        0x0A, 0x00, 0x00, 0x00, /* TLV data length */
+        0x61, 0x00, 0x62, 0x00, /* TLV data string */
+        0x63, 0x00, 0x64, 0x00,
+        0x65, 0x00, 0x00, 0x00,
+        /* Second unnamed TLV */
+        0x0A, 0x00, 0x00, 0x00, /* TLV type MBIM_TLV_TYPE_WCHAR_STR, no padding */
+        0x0C, 0x00, 0x00, 0x00, /* TLV data length */
+        0x4F, 0x00, 0x72, 0x00, /* TLV data string */
+        0x61, 0x00, 0x6E, 0x00,
+        0x67, 0x00, 0x65, 0x00,
+        /* Third unnamed TLV */
+        0x0D, 0x00, 0x00, 0x01, /* TLV type MBIM_TLV_TYPE_PCO, padding 1 */
+        0x0B, 0x00, 0x00, 0x00, /* TLV data length */
+        0x01, 0x02, 0x03, 0x04, /* TLV data bytes */
+        0x05, 0x06, 0x07, 0x08,
+        0x09, 0x0A, 0x0B, 0x00,
+    };
+
+    tlv = mbim_tlv_string_new ("abcde", &error);
+    g_assert_no_error (error);
+    g_assert (tlv);
+    tlv_list = g_list_append (tlv_list, tlv);
+
+    tlv = mbim_tlv_string_new ("Orange", &error);
+    g_assert_no_error (error);
+    g_assert (tlv);
+    tlv_list = g_list_append (tlv_list, tlv);
+
+    tlv = mbim_tlv_new (MBIM_TLV_TYPE_PCO, pco, sizeof (pco));
+    g_assert (tlv);
+    tlv_list = g_list_append (tlv_list, tlv);
+
+    message = (mbim_message_ms_basic_connect_extensions_v3_registration_parameters_set_new (
+                   MBIM_MICO_MODE_DISABLED,
+                   MBIM_DRX_CYCLE_NOT_SPECIFIED,
+                   MBIM_LADN_INFO_NOT_NEEDED,
+                   MBIM_DEFAULT_PDU_ACTIVATION_HINT_LIKELY,
+                   TRUE,
+                   tlv_list,
+                   &error));
+    g_assert_no_error (error);
+    g_assert (message != NULL);
+
+    mbim_message_set_transaction_id (message, 1);
+
+    test_message_trace ((const guint8 *)((GByteArray *)message)->data,
+                        ((GByteArray *)message)->len,
+                        expected_message,
+                        sizeof (expected_message));
+
+    g_assert_cmpuint (mbim_message_get_transaction_id (message), ==, 1);
+    g_assert_cmpuint (mbim_message_get_message_type   (message), ==, MBIM_MESSAGE_TYPE_COMMAND);
+    g_assert_cmpuint (mbim_message_get_message_length (message), ==, sizeof (expected_message));
+
+    g_assert_cmpuint (mbim_message_command_get_service      (message), ==, MBIM_SERVICE_MS_BASIC_CONNECT_EXTENSIONS);
+    g_assert_cmpuint (mbim_message_command_get_cid          (message), ==, MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_REGISTRATION_PARAMETERS);
+    g_assert_cmpuint (mbim_message_command_get_command_type (message), ==, MBIM_MESSAGE_COMMAND_TYPE_SET);
+
+    g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
+    g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
+
+    g_list_free_full (tlv_list, (GDestroyNotify)mbim_tlv_unref);
+
+    test_message_printable (message, 3, 0);
+}
+
+static void
+test_ms_basic_connect_v3_connect_set (void)
+{
+    GError *error = NULL;
+    MbimMessage *message;
+    const guint8 expected_message [] = {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x80, 0x00, 0x00, 0x00, /* length */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_message */
+        0xA2, 0x89, 0xCC, 0x33, /* service id */
+        0xBC, 0xBB, 0x8B, 0x4F,
+        0xB6, 0xB0, 0x13, 0x3E,
+        0xC2, 0xAA, 0xE6, 0xDF,
+        0x0C, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x50, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x01, 0x00, 0x00, 0x00, /* session id */
+        0x01, 0x00, 0x00, 0x00, /* activation command */
+        0x00, 0x00, 0x00, 0x00, /* compression */
+        0x01, 0x00, 0x00, 0x00, /* auth protocol */
+        0x01, 0x00, 0x00, 0x00, /* ip type */
+        0x7E, 0x5E, 0x2A, 0x7E, /* context type */
+        0x4E, 0x6F, 0x72, 0x72,
+        0x73, 0x6B, 0x65, 0x6E,
+        0x7E, 0x5E, 0x2A, 0x7E,
+        0x01, 0x00, 0x00, 0x00, /* media preference */
+        0x0A, 0x00, 0x00, 0x00, /* access string */
+        0x10, 0x00, 0x00, 0x00,
+        0x69, 0x00, 0x6E, 0x00,
+        0x74, 0x00, 0x65, 0x00,
+        0x72, 0x00, 0x6E, 0x00,
+        0x65, 0x00, 0x74, 0x00,
+        0x0A, 0x00, 0x00, 0x00, /* username */
+        0x00, 0x00, 0x00, 0x00,
+        0x0A, 0x00, 0x00, 0x00, /* password */
+        0x00, 0x00, 0x00, 0x00,
+        /* no unnamed TLVs */
+    };
+
+    /* CONNECT set message */
+    message = (mbim_message_ms_basic_connect_v3_connect_set_new (
+                   0x01,
+                   MBIM_ACTIVATION_COMMAND_ACTIVATE,
+                   MBIM_COMPRESSION_NONE,
+                   MBIM_AUTH_PROTOCOL_PAP,
+                   MBIM_CONTEXT_IP_TYPE_IPV4,
+                   mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
+                   MBIM_ACCESS_MEDIA_TYPE_3GPP,
+                   "internet",
+                   "",
+                   "",
+                   NULL, /* no unnamed ies */
+                   &error));
+
+    g_assert_no_error (error);
+    g_assert (message != NULL);
+    mbim_message_set_transaction_id (message, 1);
+
+    test_message_trace ((const guint8 *)((GByteArray *)message)->data,
+                        ((GByteArray *)message)->len,
+                        expected_message,
+                        sizeof (expected_message));
+
+    g_assert_cmpuint (mbim_message_get_transaction_id (message), ==, 1);
+    g_assert_cmpuint (mbim_message_get_message_type   (message), ==, MBIM_MESSAGE_TYPE_COMMAND);
+    g_assert_cmpuint (mbim_message_get_message_length (message), ==, sizeof (expected_message));
+
+    g_assert_cmpuint (mbim_message_command_get_service      (message), ==, MBIM_SERVICE_BASIC_CONNECT);
+    g_assert_cmpuint (mbim_message_command_get_cid          (message), ==, MBIM_CID_BASIC_CONNECT_CONNECT);
+    g_assert_cmpuint (mbim_message_command_get_command_type (message), ==, MBIM_MESSAGE_COMMAND_TYPE_SET);
+
+    g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
+    g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
+
+    test_message_printable (message, 3, 0);
+
     mbim_message_unref (message);
 }
 
@@ -1341,23 +1715,31 @@
 {
     g_test_init (&argc, &argv, NULL);
 
-    g_test_add_func ("/libmbim-glib/message/builder/basic-connect/pin/set/raw", test_message_builder_basic_connect_pin_set_raw);
-    g_test_add_func ("/libmbim-glib/message/builder/basic-connect/pin/set", test_message_builder_basic_connect_pin_set);
-    g_test_add_func ("/libmbim-glib/message/builder/basic-connect/connect/set/raw", test_message_builder_basic_connect_connect_set_raw);
-    g_test_add_func ("/libmbim-glib/message/builder/basic-connect/connect/set", test_message_builder_basic_connect_connect_set);
-    g_test_add_func ("/libmbim-glib/message/builder/basic-connect/service-activation/set", test_message_builder_basic_connect_service_activation_set);
-    g_test_add_func ("/libmbim-glib/message/builder/basic-connect/device-service-subscribe-list/set", test_message_builder_basic_connect_device_service_subscribe_list_set);
-    g_test_add_func ("/libmbim-glib/message/builder/ussd/set", test_message_builder_ussd_set);
-    g_test_add_func ("/libmbim-glib/message/builder/auth/akap/query", test_message_builder_auth_akap_query);
-    g_test_add_func ("/libmbim-glib/message/builder/stk/pac/set", test_message_builder_stk_pac_set);
-    g_test_add_func ("/libmbim-glib/message/builder/stk/terminal-response/set", test_message_builder_stk_terminal_response_set);
-    g_test_add_func ("/libmbim-glib/message/builder/stk/envelope/set", test_message_builder_stk_envelope_set);
-    g_test_add_func ("/libmbim-glib/message/builder/basic-connect/ip-packet-filters/set/none", test_message_builder_basic_connect_ip_packet_filters_set_none);
-    g_test_add_func ("/libmbim-glib/message/builder/basic-connect/ip-packet-filters/set/one", test_message_builder_basic_connect_ip_packet_filters_set_one);
-    g_test_add_func ("/libmbim-glib/message/builder/basic-connect/ip-packet-filters/set/two", test_message_builder_basic_connect_ip_packet_filters_set_two);
-    g_test_add_func ("/libmbim-glib/message/builder/dss/connect/set", test_message_builder_dss_connect_set);
-    g_test_add_func ("/libmbim-glib/message/builder/basic-connect/multicarrier-providers/set", test_message_builder_basic_connect_multicarrier_providers_set);
-    g_test_add_func ("/libmbim-glib/message/builder/ms-host-shutdown/notify/set", test_message_builder_ms_host_shutdown_notify_set);
+#define PREFIX "/libmbim-glib/message/builder"
+
+    g_test_add_func (PREFIX "/basic-connect/pin/set/raw", test_basic_connect_pin_set_raw);
+    g_test_add_func (PREFIX "/basic-connect/pin/set", test_basic_connect_pin_set);
+    g_test_add_func (PREFIX "/basic-connect/connect/set/raw", test_basic_connect_connect_set_raw);
+    g_test_add_func (PREFIX "/basic-connect/connect/set", test_basic_connect_connect_set);
+    g_test_add_func (PREFIX "/basic-connect/service-activation/set", test_basic_connect_service_activation_set);
+    g_test_add_func (PREFIX "/basic-connect/device-service-subscribe-list/set", test_basic_connect_device_service_subscribe_list_set);
+    g_test_add_func (PREFIX "/ussd/set", test_ussd_set);
+    g_test_add_func (PREFIX "/auth/akap/query", test_auth_akap_query);
+    g_test_add_func (PREFIX "/stk/pac/set", test_stk_pac_set);
+    g_test_add_func (PREFIX "/stk/terminal-response/set", test_stk_terminal_response_set);
+    g_test_add_func (PREFIX "/stk/envelope/set", test_stk_envelope_set);
+    g_test_add_func (PREFIX "/basic-connect/ip-packet-filters/set/none", test_basic_connect_ip_packet_filters_set_none);
+    g_test_add_func (PREFIX "/basic-connect/ip-packet-filters/set/one", test_basic_connect_ip_packet_filters_set_one);
+    g_test_add_func (PREFIX "/basic-connect/ip-packet-filters/set/two", test_basic_connect_ip_packet_filters_set_two);
+    g_test_add_func (PREFIX "/dss/connect/set", test_dss_connect_set);
+    g_test_add_func (PREFIX "/basic-connect/multicarrier-providers/set", test_basic_connect_multicarrier_providers_set);
+    g_test_add_func (PREFIX "/ms-host-shutdown/notify/set", test_ms_host_shutdown_notify_set);
+    g_test_add_func (PREFIX "/ms-basic-connect-extensions/registration-parameters/set/0-unnamed-tlvs", test_ms_basic_connect_extensions_registration_parameters_set_0_unnamed_tlvs);
+    g_test_add_func (PREFIX "/ms-basic-connect-extensions/registration-parameters/set/1-unnamed-tlv", test_ms_basic_connect_extensions_registration_parameters_set_1_unnamed_tlv);
+    g_test_add_func (PREFIX "/ms-basic-connect-extensions/registration-parameters/set/3-unnamed-tlvs", test_ms_basic_connect_extensions_registration_parameters_set_3_unnamed_tlvs);
+    g_test_add_func (PREFIX "/ms-basic-connect-v3/connect/set", test_ms_basic_connect_v3_connect_set);
+
+#undef PREFIX
 
     return g_test_run ();
 }
diff --git a/src/libmbim-glib/test/test-message-parser.c b/src/libmbim-glib/test/test-message-parser.c
index e3e2730..afdab19 100644
--- a/src/libmbim-glib/test/test-message-parser.c
+++ b/src/libmbim-glib/test/test-message-parser.c
@@ -13,12 +13,13 @@
 #include "mbim-auth.h"
 #include "mbim-stk.h"
 #include "mbim-ms-firmware-id.h"
+#include "mbim-ms-basic-connect-extensions.h"
 #include "mbim-message.h"
+#include "mbim-tlv.h"
 #include "mbim-cid.h"
 #include "mbim-common.h"
 #include "mbim-error-types.h"
 
-#if defined ENABLE_TEST_MESSAGE_TRACES
 static void
 test_message_trace (const guint8 *computed,
                     guint32       computed_size,
@@ -51,12 +52,28 @@
         }
     }
 }
-#else
-#define test_message_trace(...)
-#endif
 
 static void
-test_message_parser_basic_connect_visible_providers (void)
+test_message_printable (MbimMessage *message,
+                        guint8       mbimex_version_major,
+                        guint8       mbimex_version_minor)
+{
+    g_autofree gchar *printable = NULL;
+
+    printable = mbim_message_get_printable_full (message,
+                                                 mbimex_version_major,
+                                                 mbimex_version_minor,
+                                                 "---- ",
+                                                 FALSE,
+                                                 NULL);
+    g_print ("\n"
+             "Message printable:\n"
+             "%s\n",
+             printable);
+}
+
+static void
+test_basic_connect_visible_providers (void)
 {
     guint32 n_providers;
     g_autoptr(GError) error = NULL;
@@ -117,6 +134,7 @@
         0x67, 0x00, 0x65, 0x00 };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_visible_providers_response_parse (
                   response,
@@ -162,7 +180,7 @@
 }
 
 static void
-test_message_parser_basic_connect_subscriber_ready_status (void)
+test_basic_connect_subscriber_ready_status (void)
 {
     MbimSubscriberReadyState ready_state;
     MbimReadyInfoFlag ready_info;
@@ -234,6 +252,7 @@
         0x31, 0x00, 0x32, 0x00 };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_subscriber_ready_status_response_parse (
                   response,
@@ -258,7 +277,7 @@
 }
 
 static void
-test_message_parser_basic_connect_device_caps (void)
+test_basic_connect_device_caps (void)
 {
     MbimDeviceType device_type;
     MbimCellularClass cellular_class;
@@ -329,6 +348,7 @@
                                 0x4D, 0x00, 0x00, 0x00 };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_device_caps_response_parse (
                   response,
@@ -367,7 +387,7 @@
 }
 
 static void
-test_message_parser_basic_connect_ip_configuration (void)
+test_basic_connect_ip_configuration (void)
 {
     guint32 session_id;
     MbimIPConfigurationAvailableFlag ipv4configurationavailable;
@@ -428,6 +448,7 @@
     };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_ip_configuration_response_parse (
                   response,
@@ -499,7 +520,7 @@
 }
 
 static void
-test_message_parser_basic_connect_service_activation (void)
+test_basic_connect_service_activation (void)
 {
     guint32 nw_error;
     const guint8 *databuffer;
@@ -533,6 +554,7 @@
         0x05, 0x06, 0x07, 0x08  };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_service_activation_response_parse (
                   response,
@@ -549,7 +571,7 @@
 }
 
 static void
-test_message_parser_basic_connect_register_state (void)
+test_basic_connect_register_state (void)
 {
     MbimNwError nw_error;
     MbimRegisterState register_state;
@@ -598,6 +620,7 @@
         0x36, 0x00, 0x00, 0x00 };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_register_state_response_parse (
                   response,
@@ -614,7 +637,7 @@
 
     g_assert_no_error (error);
 
-    g_assert_cmpuint (nw_error, ==, MBIM_NW_ERROR_UNKNOWN);
+    g_assert_cmpuint (nw_error, ==, MBIM_NW_ERROR_NONE);
     g_assert_cmpuint (register_state, ==, MBIM_REGISTER_STATE_HOME);
     g_assert_cmpuint (register_mode, ==, MBIM_REGISTER_MODE_AUTOMATIC);
     g_assert_cmpuint (available_data_classes, ==, (MBIM_DATA_CLASS_UMTS |
@@ -628,7 +651,7 @@
 }
 
 static void
-test_message_parser_provisioned_contexts (void)
+test_provisioned_contexts (void)
 {
     guint32 provisioned_contexts_count = 0;
     g_autoptr(MbimProvisionedContextElementArray) provisioned_contexts = NULL;
@@ -654,6 +677,8 @@
     };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
+
     g_assert (!mbim_message_provisioned_contexts_response_parse (
                   response,
                   &provisioned_contexts_count,
@@ -664,7 +689,7 @@
 }
 
 static void
-test_message_parser_sms_read_zero_pdu (void)
+test_sms_read_zero_pdu (void)
 {
     MbimSmsFormat format;
     guint32 messages_count;
@@ -695,6 +720,7 @@
     };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_sms_read_response_parse (
                   response,
@@ -712,7 +738,7 @@
 }
 
 static void
-test_message_parser_sms_read_single_pdu (void)
+test_sms_read_single_pdu (void)
 {
     MbimSmsFormat format;
     guint32 messages_count;
@@ -762,6 +788,7 @@
     };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_sms_read_response_parse (
                   response,
@@ -788,7 +815,7 @@
 }
 
 static void
-test_message_parser_sms_read_multiple_pdu (void)
+test_sms_read_multiple_pdu (void)
 {
     guint32 idx;
     MbimSmsFormat format;
@@ -858,6 +885,7 @@
     };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_sms_read_response_parse (
                   response,
@@ -907,7 +935,7 @@
 }
 
 static void
-test_message_parser_ussd (void)
+test_ussd (void)
 {
     MbimUssdResponse ussd_response;
     MbimUssdSessionState ussd_session_state;
@@ -954,6 +982,7 @@
     };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_ussd_response_parse (
                   response,
@@ -978,7 +1007,7 @@
 }
 
 static void
-test_message_parser_auth_akap (void)
+test_auth_akap (void)
 {
     const guint8 *res;
     guint32 res_len;
@@ -1049,8 +1078,8 @@
         0x73, 0x72
     };
 
-
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_auth_akap_response_parse (
                   response,
@@ -1090,7 +1119,7 @@
 }
 
 static void
-test_message_parser_stk_pac_notification (void)
+test_stk_pac_notification (void)
 {
     const guint8 *databuffer;
     guint32 databuffer_len;
@@ -1139,6 +1168,7 @@
     };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_stk_pac_notification_parse (
                   response,
@@ -1159,7 +1189,7 @@
 }
 
 static void
-test_message_parser_stk_pac_response (void)
+test_stk_pac_response (void)
 {
     const guint8 *databuffer;
     g_autoptr(GError) error = NULL;
@@ -1316,6 +1346,7 @@
     };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_stk_pac_response_parse (
                   response,
@@ -1331,7 +1362,7 @@
 }
 
 static void
-test_message_parser_stk_terminal_response (void)
+test_stk_terminal_response (void)
 {
     const guint8 *databuffer;
     guint32 databuffer_len;
@@ -1372,6 +1403,7 @@
     };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_stk_terminal_response_response_parse (
                   response,
@@ -1392,7 +1424,7 @@
 }
 
 static void
-test_message_parser_stk_envelope_response (void)
+test_stk_envelope_response (void)
 {
     const guint8 *databuffer;
     g_autoptr(GError) error = NULL;
@@ -1437,6 +1469,7 @@
     };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_stk_envelope_response_parse (
                   response,
@@ -1452,7 +1485,7 @@
 }
 
 static void
-test_message_parser_basic_connect_ip_packet_filters_none (void)
+test_basic_connect_ip_packet_filters_none (void)
 {
     guint32 n_filters;
     guint32 session_id;
@@ -1482,6 +1515,7 @@
     };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_ip_packet_filters_response_parse (
                   response,
@@ -1497,7 +1531,7 @@
 }
 
 static void
-test_message_parser_basic_connect_ip_packet_filters_one (void)
+test_basic_connect_ip_packet_filters_one (void)
 {
     guint32 n_filters;
     guint32 session_id;
@@ -1546,6 +1580,7 @@
     };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_ip_packet_filters_response_parse (
                   response,
@@ -1573,7 +1608,7 @@
 }
 
 static void
-test_message_parser_basic_connect_ip_packet_filters_two (void)
+test_basic_connect_ip_packet_filters_two (void)
 {
     guint32 n_filters;
     guint32 session_id;
@@ -1644,6 +1679,7 @@
     };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_ip_packet_filters_response_parse (
                   response,
@@ -1679,7 +1715,7 @@
 }
 
 static void
-test_message_parser_ms_firmware_id_get (void)
+test_ms_firmware_id_get (void)
 {
     const MbimUuid *firmware_id;
     g_autoptr(GError) error = NULL;
@@ -1716,6 +1752,7 @@
     };
 
     response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
 
     g_assert (mbim_message_ms_firmware_id_get_response_parse (
                   response,
@@ -1728,7 +1765,7 @@
 }
 
 static void
-test_message_parser_basic_connect_connect_short (void)
+test_basic_connect_connect_short (void)
 {
     guint32 session_id;
     MbimActivationState activation_state;
@@ -1775,7 +1812,7 @@
 }
 
 static void
-test_message_parser_basic_connect_visible_providers_overflow (void)
+test_basic_connect_visible_providers_overflow (void)
 {
     guint32 n_providers;
     g_autoptr(GError) error = NULL;
@@ -1847,32 +1884,1102 @@
     g_assert (!result);
 }
 
+static void
+test_ms_basic_connect_extensions_base_stations (void)
+{
+    g_autoptr(GError)                              error = NULL;
+    g_autoptr(MbimMessage)                         response = NULL;
+    gboolean                                       result;
+    MbimDataClass                                  system_type;
+	g_autoptr(MbimCellInfoServingGsm)              gsm_serving_cell = NULL;
+    g_autoptr(MbimCellInfoServingUmts)             umts_serving_cell = NULL;
+    g_autoptr(MbimCellInfoServingTdscdma)          tdscdma_serving_cell = NULL;
+    g_autoptr(MbimCellInfoServingLte)              lte_serving_cell = NULL;
+    guint32                                        gsm_neighboring_cells_count;
+    g_autoptr(MbimCellInfoNeighboringGsmArray)     gsm_neighboring_cells = NULL;
+    guint32                                        umts_neighboring_cells_count;
+    g_autoptr(MbimCellInfoNeighboringUmtsArray)    umts_neighboring_cells = NULL;
+    guint32                                        tdscdma_neighboring_cells_count;
+    g_autoptr(MbimCellInfoNeighboringTdscdmaArray) tdscdma_neighboring_cells = NULL;
+    guint32                                        lte_neighboring_cells_count;
+    g_autoptr(MbimCellInfoNeighboringLteArray)     lte_neighboring_cells = NULL;
+    guint32                                        cdma_cells_count;
+    g_autoptr(MbimCellInfoCdmaArray)               cdma_cells = NULL;
+
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0xD8, 0x00, 0x00, 0x00, /* length */
+        0x03, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0x3D, 0x01, 0xDC, 0xC5, /* service id */
+        0xFE, 0xF5, 0x4D, 0x05,
+        0x0D, 0x3A, 0xBE, 0xF7,
+        0x05, 0x8E, 0x9A, 0xAF,
+        0x0B, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0xA8, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x60, 0x00, 0x00, 0x00, /* system type */
+        0x00, 0x00, 0x00, 0x00, /* gsm serving cell offset */
+        0x00, 0x00, 0x00, 0x00, /* gsm serving cell size */
+        0x00, 0x00, 0x00, 0x00, /* umts serving cell offset */
+        0x00, 0x00, 0x00, 0x00, /* umts serving cell size */
+        0x00, 0x00, 0x00, 0x00, /* tdscdma serving cell offset */
+        0x00, 0x00, 0x00, 0x00, /* tdscdma serving cell size */
+        0x4C, 0x00, 0x00, 0x00, /* lte serving cell offset*/
+        0x2E, 0x00, 0x00, 0x00, /* lte serving cell size*/
+        0xA0, 0x00, 0x00, 0x00, /* gsm network measurement report offset */
+        0x04, 0x00, 0x00, 0x00, /* gsm network measurement report size */
+        0xA4, 0x00, 0x00, 0x00, /* umts network measurement report offset */
+        0x04, 0x00, 0x00, 0x00, /* umts network measurement report size */
+        0x00, 0x00, 0x00, 0x00, /* tdscdma network measurement report offset */
+        0x00, 0x00, 0x00, 0x00, /* tdscdma network measurement report size */
+        0x7C, 0x00, 0x00, 0x00, /* lte network measurement report offset */
+        0x24, 0x00, 0x00, 0x00, /* lte network measurement report size */
+        0x00, 0x00, 0x00, 0x00, /* cdma network measurement report offset */
+        0x00, 0x00, 0x00, 0x00, /* cdma network measurement report size */
+        /* lte serving cell */
+/*4C*/  0x24, 0x00, 0x00, 0x00, /* provider id offset */
+        0x0A, 0x00, 0x00, 0x00, /* provider id size */
+        0x1F, 0xCD, 0x65, 0x04, /* cell id */
+        0x00, 0x19, 0x00, 0x00, /* earfcn */
+        0x36, 0x01, 0x00, 0x00, /* physical cell id */
+        0xFE, 0x6F, 0x00, 0x00, /* tac */
+        0x99, 0xFF, 0xFF, 0xFF, /* rsrp */
+        0xF4, 0xFF, 0xFF, 0xFF, /* rsrq */
+        0xFF, 0xFF, 0xFF, 0xFF, /* timing advance */
+        0x32, 0x00, 0x31, 0x00, /* provider id string */
+        0x34, 0x00, 0x30, 0x00,
+        0x37, 0x00, 0x00, 0x00,
+        /* lte network measurement report */
+/*7C*/  0x01, 0x00, 0x00, 0x00, /* element count */
+        0x00, 0x00, 0x00, 0x00, /* provider id offset */
+        0x00, 0x00, 0x00, 0x00, /* provider id size */
+        0xFF, 0xFF, 0xFF, 0xFF, /* cell id */
+        0xFF, 0xFF, 0xFF, 0xFF, /* earfcn */
+        0x36, 0x01, 0x00, 0x00, /* physical cell id */
+        0xFF, 0xFF, 0xFF, 0xFF, /* tac */
+        0x99, 0xFF, 0xFF, 0xFF, /* rsrp */
+        0xF4, 0xFF, 0xFF, 0xFF, /* rsrq */
+        /* gsm network measurement report */
+/*A0*/  0x00, 0x00, 0x00, 0x00,
+        /* umts network measurement report */
+/*A4*/  0x00, 0x00, 0x00, 0x00
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 1, 0);
+
+    result = (mbim_message_ms_basic_connect_extensions_base_stations_info_response_parse (
+                  response,
+                  &system_type,
+                  &gsm_serving_cell,
+                  &umts_serving_cell,
+                  &tdscdma_serving_cell,
+                  &lte_serving_cell,
+                  &gsm_neighboring_cells_count,
+                  &gsm_neighboring_cells,
+                  &umts_neighboring_cells_count,
+                  &umts_neighboring_cells,
+                  &tdscdma_neighboring_cells_count,
+                  &tdscdma_neighboring_cells,
+                  &lte_neighboring_cells_count,
+                  &lte_neighboring_cells,
+                  &cdma_cells_count,
+                  &cdma_cells,
+                  &error));
+
+    g_assert_no_error (error);
+    g_assert (result);
+
+    g_assert_null (gsm_serving_cell);
+    g_assert_null (umts_serving_cell);
+    g_assert_null (tdscdma_serving_cell);
+    g_assert_nonnull (lte_serving_cell);
+    g_assert_cmpuint (gsm_neighboring_cells_count, ==, 0);
+    g_assert_null (gsm_neighboring_cells);
+    g_assert_cmpuint (umts_neighboring_cells_count, ==, 0);
+    g_assert_null (umts_neighboring_cells);
+    g_assert_cmpuint (tdscdma_neighboring_cells_count, ==, 0);
+    g_assert_null (tdscdma_neighboring_cells);
+    g_assert_cmpuint (lte_neighboring_cells_count, ==, 1);
+    g_assert_nonnull (lte_neighboring_cells);
+    g_assert_cmpuint (cdma_cells_count, ==, 0);
+    g_assert_null (cdma_cells);
+}
+
+static void
+test_ms_basic_connect_extensions_registration_parameters_0_unnamed_tlvs (void)
+{
+    g_autoptr(GError)             error = NULL;
+    g_autoptr(MbimMessage)        response = NULL;
+    gboolean                      result;
+    MbimMicoMode                  mico_mode;
+    MbimDrxCycle                  drx_cycle;
+    MbimLadnInfo                  ladn_info;
+    MbimDefaultPduActivationHint  pdu_hint;
+    gboolean                      re_register_if_needed;
+    GList                        *unnamed_ies = NULL;
+
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x44, 0x00, 0x00, 0x00, /* length */
+        0x04, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0x3D, 0x01, 0xDC, 0xC5, /* service id */
+        0xFE, 0xF5, 0x4D, 0x05,
+        0x0D, 0x3A, 0xBE, 0xF7,
+        0x05, 0x8E, 0x9A, 0xAF,
+        0x11, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x14, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x00, 0x00, 0x00, 0x00, /* mico mode */
+        0x00, 0x00, 0x00, 0x00, /* drx cycle */
+        0x00, 0x00, 0x00, 0x00, /* ladn info */
+        0x01, 0x00, 0x00, 0x00, /* pdu hint */
+        0x01, 0x00, 0x00, 0x00, /* re register if needed */
+        /* no unnamed TLVs */
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 3, 0);
+
+    result = (mbim_message_ms_basic_connect_extensions_v3_registration_parameters_response_parse (
+                  response,
+                  &mico_mode,
+                  &drx_cycle,
+                  &ladn_info,
+                  &pdu_hint,
+                  &re_register_if_needed,
+                  &unnamed_ies,
+                  &error));
+
+    g_assert_no_error (error);
+    g_assert (result);
+
+    g_assert_cmpuint (mico_mode, ==, MBIM_MICO_MODE_DISABLED);
+    g_assert_cmpuint (drx_cycle, ==, MBIM_DRX_CYCLE_NOT_SPECIFIED);
+    g_assert_cmpuint (ladn_info, ==, MBIM_LADN_INFO_NOT_NEEDED);
+    g_assert_cmpuint (pdu_hint, ==, MBIM_DEFAULT_PDU_ACTIVATION_HINT_LIKELY);
+    g_assert_cmpuint (re_register_if_needed, ==, TRUE);
+    g_assert_cmpuint (g_list_length (unnamed_ies), ==, 0);
+}
+
+static void
+test_ms_basic_connect_extensions_registration_parameters_1_unnamed_tlv (void)
+{
+    g_autoptr(GError)             error = NULL;
+    g_autoptr(MbimMessage)        response = NULL;
+    gboolean                      result;
+    MbimMicoMode                  mico_mode;
+    MbimDrxCycle                  drx_cycle;
+    MbimLadnInfo                  ladn_info;
+    MbimDefaultPduActivationHint  pdu_hint;
+    gboolean                      re_register_if_needed;
+    GList                        *unnamed_ies = NULL;
+    MbimTlv                      *tlv;
+    g_autofree gchar             *tlv_str = NULL;
+
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x58, 0x00, 0x00, 0x00, /* length */
+        0x04, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0x3D, 0x01, 0xDC, 0xC5, /* service id */
+        0xFE, 0xF5, 0x4D, 0x05,
+        0x0D, 0x3A, 0xBE, 0xF7,
+        0x05, 0x8E, 0x9A, 0xAF,
+        0x11, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x28, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x00, 0x00, 0x00, 0x00, /* mico mode */
+        0x00, 0x00, 0x00, 0x00, /* drx cycle */
+        0x00, 0x00, 0x00, 0x00, /* ladn info */
+        0x01, 0x00, 0x00, 0x00, /* pdu hint */
+        0x01, 0x00, 0x00, 0x00, /* re register if needed */
+        /* First unnamed TLV */
+        0x0A, 0x00, 0x00, 0x00, /* TLV type MBIM_TLV_TYPE_WCHAR_STR, no padding */
+        0x0C, 0x00, 0x00, 0x00, /* TLV data length */
+        0x4F, 0x00, 0x72, 0x00, /* TLV data string */
+        0x61, 0x00, 0x6E, 0x00,
+        0x67, 0x00, 0x65, 0x00,
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 3, 0);
+
+    result = (mbim_message_ms_basic_connect_extensions_v3_registration_parameters_response_parse (
+                  response,
+                  &mico_mode,
+                  &drx_cycle,
+                  &ladn_info,
+                  &pdu_hint,
+                  &re_register_if_needed,
+                  &unnamed_ies,
+                  &error));
+
+    g_assert_no_error (error);
+    g_assert (result);
+
+    g_assert_cmpuint (mico_mode, ==, MBIM_MICO_MODE_DISABLED);
+    g_assert_cmpuint (drx_cycle, ==, MBIM_DRX_CYCLE_NOT_SPECIFIED);
+    g_assert_cmpuint (ladn_info, ==, MBIM_LADN_INFO_NOT_NEEDED);
+    g_assert_cmpuint (pdu_hint, ==, MBIM_DEFAULT_PDU_ACTIVATION_HINT_LIKELY);
+    g_assert_cmpuint (re_register_if_needed, ==, TRUE);
+    g_assert_cmpuint (g_list_length (unnamed_ies), ==, 1);
+
+    tlv = (MbimTlv *)(unnamed_ies->data);
+    g_assert_cmpuint (mbim_tlv_get_tlv_type (tlv), ==, MBIM_TLV_TYPE_WCHAR_STR);
+
+    tlv_str = mbim_tlv_string_get (tlv, &error);
+    g_assert_no_error (error);
+    g_assert_cmpstr (tlv_str, ==, "Orange");
+
+    g_list_free_full (unnamed_ies, (GDestroyNotify)mbim_tlv_unref);
+}
+
+static void
+test_ms_basic_connect_extensions_registration_parameters_3_unnamed_tlvs (void)
+{
+    g_autoptr(GError)             error = NULL;
+    g_autoptr(MbimMessage)        response = NULL;
+    gboolean                      result;
+    MbimMicoMode                  mico_mode;
+    MbimDrxCycle                  drx_cycle;
+    MbimLadnInfo                  ladn_info;
+    MbimDefaultPduActivationHint  pdu_hint;
+    gboolean                      re_register_if_needed;
+    GList                        *unnamed_ies = NULL;
+    GList                        *iter;
+    MbimTlv                      *tlv;
+    g_autofree gchar             *tlv_str_1 = NULL;
+    const gchar                  *expected_tlv_str_1 = "abcde";
+    g_autofree gchar             *tlv_str_2 = NULL;
+    const gchar                  *expected_tlv_str_2 = "Orange";
+    const guint8                 *pco_3 = NULL;
+    guint32                       pco_3_size = 0;
+    const guint8                  expected_pco[] = { 0x01, 0x02, 0x03, 0x04,
+                                                     0x05, 0x06, 0x07, 0x08,
+                                                     0x09, 0x0A, 0x0B };
+
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x80, 0x00, 0x00, 0x00, /* length */
+        0x04, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0x3D, 0x01, 0xDC, 0xC5, /* service id */
+        0xFE, 0xF5, 0x4D, 0x05,
+        0x0D, 0x3A, 0xBE, 0xF7,
+        0x05, 0x8E, 0x9A, 0xAF,
+        0x11, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x50, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x00, 0x00, 0x00, 0x00, /* mico mode */
+        0x00, 0x00, 0x00, 0x00, /* drx cycle */
+        0x00, 0x00, 0x00, 0x00, /* ladn info */
+        0x01, 0x00, 0x00, 0x00, /* pdu hint */
+        0x01, 0x00, 0x00, 0x00, /* re register if needed */
+        /* First unnamed TLV */
+        0x0A, 0x00, 0x00, 0x02, /* TLV type MBIM_TLV_TYPE_WCHAR_STR, padding 2 */
+        0x0A, 0x00, 0x00, 0x00, /* TLV data length */
+        0x61, 0x00, 0x62, 0x00, /* TLV data string */
+        0x63, 0x00, 0x64, 0x00,
+        0x65, 0x00, 0x00, 0x00,
+        /* Second unnamed TLV */
+        0x0A, 0x00, 0x00, 0x00, /* TLV type MBIM_TLV_TYPE_WCHAR_STR, no padding */
+        0x0C, 0x00, 0x00, 0x00, /* TLV data length */
+        0x4F, 0x00, 0x72, 0x00, /* TLV data string */
+        0x61, 0x00, 0x6E, 0x00,
+        0x67, 0x00, 0x65, 0x00,
+        /* Third unnamed TLV */
+        0x0D, 0x00, 0x00, 0x01, /* TLV type MBIM_TLV_TYPE_PCO, padding 1 */
+        0x0B, 0x00, 0x00, 0x00, /* TLV data length */
+        0x01, 0x02, 0x03, 0x04, /* TLV data bytes */
+        0x05, 0x06, 0x07, 0x08,
+        0x09, 0x0A, 0x0B, 0x00,
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 3, 0);
+
+    result = (mbim_message_ms_basic_connect_extensions_v3_registration_parameters_response_parse (
+                  response,
+                  &mico_mode,
+                  &drx_cycle,
+                  &ladn_info,
+                  &pdu_hint,
+                  &re_register_if_needed,
+                  &unnamed_ies,
+                  &error));
+
+    g_assert_no_error (error);
+    g_assert (result);
+
+    g_assert_cmpuint (mico_mode, ==, MBIM_MICO_MODE_DISABLED);
+    g_assert_cmpuint (drx_cycle, ==, MBIM_DRX_CYCLE_NOT_SPECIFIED);
+    g_assert_cmpuint (ladn_info, ==, MBIM_LADN_INFO_NOT_NEEDED);
+    g_assert_cmpuint (pdu_hint, ==, MBIM_DEFAULT_PDU_ACTIVATION_HINT_LIKELY);
+    g_assert_cmpuint (re_register_if_needed, ==, TRUE);
+    g_assert_cmpuint (g_list_length (unnamed_ies), ==, 3);
+
+    iter = unnamed_ies;
+    tlv = (MbimTlv *)(iter->data);
+    g_assert_cmpuint (mbim_tlv_get_tlv_type (tlv), ==, MBIM_TLV_TYPE_WCHAR_STR);
+    tlv_str_1 = mbim_tlv_string_get (tlv, &error);
+    g_assert_no_error (error);
+    g_assert_cmpstr (tlv_str_1, ==, expected_tlv_str_1);
+
+    iter = g_list_next (iter);
+    tlv = (MbimTlv *)(iter->data);
+    g_assert_cmpuint (mbim_tlv_get_tlv_type (tlv), ==, MBIM_TLV_TYPE_WCHAR_STR);
+    tlv_str_2 = mbim_tlv_string_get (tlv, &error);
+    g_assert_no_error (error);
+    g_assert_cmpstr (tlv_str_2, ==, expected_tlv_str_2);
+
+    iter = g_list_next (iter);
+    tlv = (MbimTlv *)(iter->data);
+    g_assert_cmpuint (mbim_tlv_get_tlv_type (tlv), ==, MBIM_TLV_TYPE_PCO);
+    pco_3 = mbim_tlv_get_tlv_data (tlv, &pco_3_size);
+    g_assert_cmpuint (pco_3_size, ==, sizeof (expected_pco));
+    g_assert (memcmp (pco_3, expected_pco, sizeof (expected_pco)) == 0);
+
+    g_list_free_full (unnamed_ies, (GDestroyNotify)mbim_tlv_unref);
+}
+
+static void
+test_ms_basic_connect_v3_connect_0_unnamed_tlvs (void)
+{
+    g_autoptr(GError)       error = NULL;
+    g_autoptr(MbimMessage)  response = NULL;
+    gboolean                result;
+    GList                  *unnamed_ies = NULL;
+    guint32                 session_id;
+    MbimActivationState     activation_state;
+    MbimVoiceCallState      voice_call_state;
+    MbimContextIpType       ip_type;
+    MbimAccessMediaType     media_type = MBIM_ACCESS_MEDIA_TYPE_UNKNOWN;
+    g_autofree gchar       *access_string = NULL;
+    const MbimUuid         *context_type;
+    guint32                 nw_error;
+
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x6C, 0x00, 0x00, 0x00, /* length */
+        0x04, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0xA2, 0x89, 0xCC, 0x33, /* service id */
+        0xBC, 0xBB, 0x8B, 0x4F,
+        0xB6, 0xB0, 0x13, 0x3E,
+        0xC2, 0xAA, 0xE6, 0xDF,
+        0x0C, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x3C, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x01, 0x00, 0x00, 0x00, /* session id */
+        0x01, 0x00, 0x00, 0x00, /* activation state */
+        0x00, 0x00, 0x00, 0x00, /* voice call state */
+        0x01, 0x00, 0x00, 0x00, /* ip type */
+        0x7E, 0x5E, 0x2A, 0x7E, /* context type */
+        0x4E, 0x6F, 0x72, 0x72,
+        0x73, 0x6B, 0x65, 0x6E,
+        0x7E, 0x5E, 0x2A, 0x7E,
+        0x00, 0x00, 0x00, 0x00, /* nw error */
+        0x01, 0x00, 0x00, 0x00, /* media type */
+        0x0A, 0x00, 0x00, 0x00, /* access string */
+        0x10, 0x00, 0x00, 0x00,
+        0x69, 0x00, 0x6E, 0x00,
+        0x74, 0x00, 0x65, 0x00,
+        0x72, 0x00, 0x6E, 0x00,
+        0x65, 0x00, 0x74, 0x00,
+        /* no unnamed TLVs */
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 3, 0);
+
+    result = (mbim_message_ms_basic_connect_v3_connect_response_parse (
+                  response,
+                  &session_id,
+                  &activation_state,
+                  &voice_call_state,
+                  &ip_type,
+                  &context_type,
+                  &nw_error,
+                  &media_type,
+                  &access_string,
+                  &unnamed_ies,
+                  &error));
+
+    g_assert_no_error (error);
+    g_assert (result);
+
+    g_assert_cmpuint (session_id, ==, 1);
+    g_assert_cmpuint (activation_state, ==, MBIM_ACTIVATION_STATE_ACTIVATED);
+    g_assert_cmpuint (voice_call_state, ==, MBIM_VOICE_CALL_STATE_NONE);
+    g_assert_cmpuint (ip_type, ==, MBIM_CONTEXT_IP_TYPE_IPV4);
+    g_assert_cmpuint (mbim_uuid_to_context_type (context_type), ==, MBIM_CONTEXT_TYPE_INTERNET);
+    g_assert_cmpuint (media_type, ==, MBIM_ACCESS_MEDIA_TYPE_3GPP);
+    g_assert_cmpstr  (access_string, ==, "internet");
+    g_assert_cmpuint (g_list_length (unnamed_ies), ==, 0);
+}
+
+static void
+test_ms_basic_connect_v3_connect_1_unnamed_tlv (void)
+{
+    g_autoptr(GError)       error = NULL;
+    g_autoptr(MbimMessage)  response = NULL;
+    gboolean                result;
+    GList                  *unnamed_ies = NULL;
+    guint32                 session_id;
+    MbimActivationState     activation_state;
+    MbimVoiceCallState      voice_call_state;
+    MbimContextIpType       ip_type;
+    MbimAccessMediaType     media_type = MBIM_ACCESS_MEDIA_TYPE_UNKNOWN;
+    g_autofree gchar       *access_string = NULL;
+    const MbimUuid         *context_type;
+    guint32                 nw_error;
+    MbimTlv                *tlv;
+    g_autofree gchar       *tlv_str = NULL;
+
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x82, 0x00, 0x00, 0x00, /* length */
+        0x04, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0xA2, 0x89, 0xCC, 0x33, /* service id */
+        0xBC, 0xBB, 0x8B, 0x4F,
+        0xB6, 0xB0, 0x13, 0x3E,
+        0xC2, 0xAA, 0xE6, 0xDF,
+        0x0C, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x52, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x01, 0x00, 0x00, 0x00, /* session id */
+        0x01, 0x00, 0x00, 0x00, /* activation state */
+        0x00, 0x00, 0x00, 0x00, /* voice call state */
+        0x01, 0x00, 0x00, 0x00, /* ip type */
+        0x7E, 0x5E, 0x2A, 0x7E, /* context type */
+        0x4E, 0x6F, 0x72, 0x72,
+        0x73, 0x6B, 0x65, 0x6E,
+        0x7E, 0x5E, 0x2A, 0x7E,
+        0x00, 0x00, 0x00, 0x00, /* nw error */
+        0x01, 0x00, 0x00, 0x00, /* media type */
+        0x0A, 0x00, 0x00, 0x00, /* access string */
+        0x10, 0x00, 0x00, 0x00,
+        0x69, 0x00, 0x6E, 0x00,
+        0x74, 0x00, 0x65, 0x00,
+        0x72, 0x00, 0x6E, 0x00,
+        0x65, 0x00, 0x74, 0x00,
+        /* First unnamed TLV */
+        0x0A, 0x00, 0x00, 0x00, /* TLV type MBIM_TLV_TYPE_WCHAR_STR, no padding */
+        0x0C, 0x00, 0x00, 0x00, /* TLV data length */
+        0x4F, 0x00, 0x72, 0x00, /* TLV data string */
+        0x61, 0x00, 0x6E, 0x00,
+        0x67, 0x00, 0x65, 0x00,
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 3, 0);
+
+    result = (mbim_message_ms_basic_connect_v3_connect_response_parse (
+                  response,
+                  &session_id,
+                  &activation_state,
+                  &voice_call_state,
+                  &ip_type,
+                  &context_type,
+                  &nw_error,
+                  &media_type,
+                  &access_string,
+                  &unnamed_ies,
+                  &error));
+
+    g_assert_no_error (error);
+    g_assert (result);
+
+    g_assert_cmpuint (session_id, ==, 1);
+    g_assert_cmpuint (activation_state, ==, MBIM_ACTIVATION_STATE_ACTIVATED);
+    g_assert_cmpuint (voice_call_state, ==, MBIM_VOICE_CALL_STATE_NONE);
+    g_assert_cmpuint (ip_type, ==, MBIM_CONTEXT_IP_TYPE_IPV4);
+    g_assert_cmpuint (mbim_uuid_to_context_type (context_type), ==, MBIM_CONTEXT_TYPE_INTERNET);
+    g_assert_cmpuint (media_type, ==, MBIM_ACCESS_MEDIA_TYPE_3GPP);
+    g_assert_cmpstr  (access_string, ==, "internet");
+    g_assert_cmpuint (g_list_length (unnamed_ies), ==, 1);
+
+    tlv = (MbimTlv *)(unnamed_ies->data);
+    g_assert_cmpuint (mbim_tlv_get_tlv_type (tlv), ==, MBIM_TLV_TYPE_WCHAR_STR);
+
+    tlv_str = mbim_tlv_string_get (tlv, &error);
+    g_assert_no_error (error);
+    g_assert_cmpstr (tlv_str, ==, "Orange");
+
+    g_list_free_full (unnamed_ies, (GDestroyNotify)mbim_tlv_unref);
+}
+
+static void
+test_ms_basic_connect_v3_connect_3_unnamed_tlvs (void)
+{
+    g_autoptr(GError)       error = NULL;
+    g_autoptr(MbimMessage)  response = NULL;
+    gboolean                result;
+    GList                  *unnamed_ies = NULL;
+    guint32                 session_id;
+    MbimActivationState     activation_state;
+    MbimVoiceCallState      voice_call_state;
+    MbimContextIpType       ip_type;
+    MbimAccessMediaType     media_type = MBIM_ACCESS_MEDIA_TYPE_UNKNOWN;
+    g_autofree gchar       *access_string = NULL;
+    const MbimUuid         *context_type;
+    guint32                 nw_error;
+    GList                  *iter;
+    MbimTlv                *tlv;
+    g_autofree gchar       *tlv_str_1 = NULL;
+    const gchar            *expected_tlv_str_1 = "abcde";
+    g_autofree gchar       *tlv_str_2 = NULL;
+    const gchar            *expected_tlv_str_2 = "Orange";
+    const guint8           *pco_3 = NULL;
+    guint32                 pco_3_size = 0;
+    const guint8            expected_pco[] = { 0x01, 0x02, 0x03, 0x04,
+                                               0x05, 0x06, 0x07, 0x08,
+                                               0x09, 0x0A, 0x0B };
+
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0xAA, 0x00, 0x00, 0x00, /* length */
+        0x04, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0xA2, 0x89, 0xCC, 0x33, /* service id */
+        0xBC, 0xBB, 0x8B, 0x4F,
+        0xB6, 0xB0, 0x13, 0x3E,
+        0xC2, 0xAA, 0xE6, 0xDF,
+        0x0C, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x7A, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x01, 0x00, 0x00, 0x00, /* session id */
+        0x01, 0x00, 0x00, 0x00, /* activation state */
+        0x00, 0x00, 0x00, 0x00, /* voice call state */
+        0x01, 0x00, 0x00, 0x00, /* ip type */
+        0x7E, 0x5E, 0x2A, 0x7E, /* context type */
+        0x4E, 0x6F, 0x72, 0x72,
+        0x73, 0x6B, 0x65, 0x6E,
+        0x7E, 0x5E, 0x2A, 0x7E,
+        0x00, 0x00, 0x00, 0x00, /* nw error */
+        0x01, 0x00, 0x00, 0x00, /* media type */
+        0x0A, 0x00, 0x00, 0x00, /* access string */
+        0x10, 0x00, 0x00, 0x00,
+        0x69, 0x00, 0x6E, 0x00,
+        0x74, 0x00, 0x65, 0x00,
+        0x72, 0x00, 0x6E, 0x00,
+        0x65, 0x00, 0x74, 0x00,
+        /* First unnamed TLV */
+        0x0A, 0x00, 0x00, 0x02, /* TLV type MBIM_TLV_TYPE_WCHAR_STR, padding 2 */
+        0x0A, 0x00, 0x00, 0x00, /* TLV data length */
+        0x61, 0x00, 0x62, 0x00, /* TLV data string */
+        0x63, 0x00, 0x64, 0x00,
+        0x65, 0x00, 0x00, 0x00,
+        /* Second unnamed TLV */
+        0x0A, 0x00, 0x00, 0x00, /* TLV type MBIM_TLV_TYPE_WCHAR_STR, no padding */
+        0x0C, 0x00, 0x00, 0x00, /* TLV data length */
+        0x4F, 0x00, 0x72, 0x00, /* TLV data string */
+        0x61, 0x00, 0x6E, 0x00,
+        0x67, 0x00, 0x65, 0x00,
+        /* Third unnamed TLV */
+        0x0D, 0x00, 0x00, 0x01, /* TLV type MBIM_TLV_TYPE_PCO, padding 1 */
+        0x0B, 0x00, 0x00, 0x00, /* TLV data length */
+        0x01, 0x02, 0x03, 0x04, /* TLV data bytes */
+        0x05, 0x06, 0x07, 0x08,
+        0x09, 0x0A, 0x0B, 0x00,
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 3, 0);
+
+    result = (mbim_message_ms_basic_connect_v3_connect_response_parse (
+                  response,
+                  &session_id,
+                  &activation_state,
+                  &voice_call_state,
+                  &ip_type,
+                  &context_type,
+                  &nw_error,
+                  &media_type,
+                  &access_string,
+                  &unnamed_ies,
+                  &error));
+
+    g_assert_no_error (error);
+    g_assert (result);
+
+    g_assert_cmpuint (session_id, ==, 1);
+    g_assert_cmpuint (activation_state, ==, MBIM_ACTIVATION_STATE_ACTIVATED);
+    g_assert_cmpuint (voice_call_state, ==, MBIM_VOICE_CALL_STATE_NONE);
+    g_assert_cmpuint (ip_type, ==, MBIM_CONTEXT_IP_TYPE_IPV4);
+    g_assert_cmpuint (mbim_uuid_to_context_type (context_type), ==, MBIM_CONTEXT_TYPE_INTERNET);
+    g_assert_cmpuint (media_type, ==, MBIM_ACCESS_MEDIA_TYPE_3GPP);
+    g_assert_cmpstr  (access_string, ==, "internet");
+    g_assert_cmpuint (g_list_length (unnamed_ies), ==, 3);
+
+
+    iter = unnamed_ies;
+    tlv = (MbimTlv *)(iter->data);
+    g_assert_cmpuint (mbim_tlv_get_tlv_type (tlv), ==, MBIM_TLV_TYPE_WCHAR_STR);
+    tlv_str_1 = mbim_tlv_string_get (tlv, &error);
+    g_assert_no_error (error);
+    g_assert_cmpstr (tlv_str_1, ==, expected_tlv_str_1);
+
+    iter = g_list_next (iter);
+    tlv = (MbimTlv *)(iter->data);
+    g_assert_cmpuint (mbim_tlv_get_tlv_type (tlv), ==, MBIM_TLV_TYPE_WCHAR_STR);
+    tlv_str_2 = mbim_tlv_string_get (tlv, &error);
+    g_assert_no_error (error);
+    g_assert_cmpstr (tlv_str_2, ==, expected_tlv_str_2);
+
+    iter = g_list_next (iter);
+    tlv = (MbimTlv *)(iter->data);
+    g_assert_cmpuint (mbim_tlv_get_tlv_type (tlv), ==, MBIM_TLV_TYPE_PCO);
+    pco_3 = mbim_tlv_get_tlv_data (tlv, &pco_3_size);
+    g_assert_cmpuint (pco_3_size, ==, sizeof (expected_pco));
+    g_assert (memcmp (pco_3, expected_pco, sizeof (expected_pco)) == 0);
+
+    g_list_free_full (unnamed_ies, (GDestroyNotify)mbim_tlv_unref);
+}
+
+static void
+test_ms_basic_connect_extensions_device_caps_v3 (void)
+{
+    g_autoptr(GError)       error = NULL;
+    g_autoptr(MbimMessage)  response = NULL;
+    gboolean                result;
+    MbimDeviceType          device_type;
+    MbimVoiceClass          voice_class;
+    MbimCellularClass       cellular_class;
+    MbimSimClass            sim_class;
+    MbimDataClassV3         data_class;
+    MbimDataSubclass        data_subclass;
+    MbimSmsCaps             sms_caps;
+    MbimCtrlCaps            ctrl_caps;
+    guint32                 max_sessions;
+    guint32                 wcdma_band_class = 0;
+    guint32                 lte_band_class_array_size = 0;
+    g_autofree guint16     *lte_band_class_array = NULL;
+    guint32                 nr_band_class_array_size = 0;
+    g_autofree guint16     *nr_band_class_array = NULL;
+    g_autofree gchar       *custom_data_class = NULL;
+    g_autofree gchar       *device_id = NULL;
+    g_autofree gchar       *firmware_info = NULL;
+    g_autofree gchar       *hardware_info = NULL;
+    guint32                 executor_index;
+    static const guint16    expected_lte_band_class_array[] = {
+        1, 2, 3, 4, 5, 7, 8, 12, 13, 14, 17, 18, 19, 20, 25, 26, 28, 29, 30, 32, 34, 38, 39, 40, 41, 42, 43, 46, 48
+    };
+    static const guint16    expected_nr_band_class_array[] = {
+        1, 2, 3, 5, 7, 8, 20, 25, 28, 30, 38, 40, 41, 48, 66, 71, 77, 78, 79
+    };
+
+    const guint8 buffer [] =  {
+        0x03, 0x00, 0x00, 0x80, 0x68, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x3D, 0x01, 0xDC, 0xC5, 0xFE, 0xF5, 0x4D, 0x05, 0x0D, 0x3A, 0xBE, 0xF7,
+        0x05, 0x8E, 0x9A, 0xAF, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00,
+        0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x7C, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00,
+        0x0B, 0x00, 0x00, 0x02, 0x3A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00,
+        0x05, 0x00, 0x07, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x11, 0x00, 0x12, 0x00,
+        0x13, 0x00, 0x14, 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x00, 0x20, 0x00,
+        0x22, 0x00, 0x26, 0x00, 0x27, 0x00, 0x28, 0x00, 0x29, 0x00, 0x2A, 0x00, 0x2B, 0x00, 0x2E, 0x00,
+        0x30, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x02, 0x26, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00,
+        0x03, 0x00, 0x05, 0x00, 0x07, 0x00, 0x08, 0x00, 0x14, 0x00, 0x19, 0x00, 0x1C, 0x00, 0x1E, 0x00,
+        0x26, 0x00, 0x28, 0x00, 0x29, 0x00, 0x30, 0x00, 0x42, 0x00, 0x47, 0x00, 0x4D, 0x00, 0x4E, 0x00,
+        0x4F, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x02, 0x0A, 0x00, 0x00, 0x00, 0x48, 0x00, 0x53, 0x00,
+        0x50, 0x00, 0x41, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x00,
+        0x38, 0x00, 0x36, 0x00, 0x32, 0x00, 0x31, 0x00, 0x34, 0x00, 0x36, 0x00, 0x30, 0x00, 0x35, 0x00,
+        0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x34, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x00, 0x00,
+        0x0A, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x31, 0x00, 0x36, 0x00, 0x30, 0x00,
+        0x30, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x2E, 0x00, 0x39, 0x00,
+        0x39, 0x00, 0x2E, 0x00, 0x32, 0x00, 0x39, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x37, 0x00, 0x2E, 0x00,
+        0x31, 0x00, 0x39, 0x00, 0x5F, 0x00, 0x47, 0x00, 0x43, 0x00, 0x0D, 0x00, 0x0A, 0x00, 0x42, 0x00,
+        0x39, 0x00, 0x30, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x56, 0x00, 0x31, 0x00,
+        0x2E, 0x00, 0x30, 0x00, 0x2E, 0x00, 0x36, 0x00
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 3, 0);
+
+    result = (mbim_message_ms_basic_connect_extensions_v3_device_caps_response_parse (
+                response,
+                &device_type,
+                &cellular_class,
+                &voice_class,
+                &sim_class,
+                &data_class,
+                &sms_caps,
+                &ctrl_caps,
+                &data_subclass,
+                &max_sessions,
+                &executor_index,
+                &wcdma_band_class,
+                &lte_band_class_array_size,
+                &lte_band_class_array,
+                &nr_band_class_array_size,
+                &nr_band_class_array,
+                &custom_data_class,
+                &device_id,
+                &firmware_info,
+                &hardware_info,
+                &error));
+
+    g_assert_no_error (error);
+    g_assert (result);
+
+    g_assert_cmpuint (device_type, ==, MBIM_DEVICE_TYPE_EMBEDDED);
+    g_assert_cmpuint (cellular_class, ==, MBIM_CELLULAR_CLASS_GSM);
+    g_assert_cmpuint (voice_class, ==, MBIM_VOICE_CLASS_NO_VOICE);
+    g_assert_cmpuint (sim_class, ==, MBIM_SIM_CLASS_REMOVABLE);
+    g_assert_cmpuint (data_class, ==, (MBIM_DATA_CLASS_V3_UMTS |
+                                       MBIM_DATA_CLASS_V3_HSDPA |
+                                       MBIM_DATA_CLASS_V3_HSUPA |
+                                       MBIM_DATA_CLASS_V3_LTE |
+                                       MBIM_DATA_CLASS_V3_5G |
+                                       MBIM_DATA_CLASS_V3_CUSTOM));
+    g_assert_cmpuint (sms_caps, ==, (MBIM_SMS_CAPS_PDU_RECEIVE |
+                                     MBIM_SMS_CAPS_PDU_SEND));
+    g_assert_cmpuint (ctrl_caps, ==, (MBIM_CTRL_CAPS_REG_MANUAL |
+                                      MBIM_CTRL_CAPS_HW_RADIO_SWITCH |
+                                      MBIM_CTRL_CAPS_ESIM |
+                                      MBIM_CTRL_CAPS_SIM_HOT_SWAP_CAPABLE));
+    g_assert_cmpuint (data_subclass, ==, (MBIM_DATA_SUBCLASS_5G_ENDC |
+                                          MBIM_DATA_SUBCLASS_5G_NR));
+    g_assert_cmpuint (max_sessions, ==, 2);
+    g_assert_cmpuint (executor_index, ==, 0);
+    g_assert_cmpuint (wcdma_band_class, ==, (1 << (1 - 1) |
+                                             1 << (2 - 1) |
+                                             1 << (4 - 1) |
+                                             1 << (5 - 1) |
+                                             1 << (8 - 1)));
+    g_assert_cmpuint (G_N_ELEMENTS (expected_lte_band_class_array), ==, lte_band_class_array_size);
+    g_assert (memcmp (lte_band_class_array, expected_lte_band_class_array, lte_band_class_array_size * sizeof (guint16)) == 0);
+    g_assert_cmpuint (G_N_ELEMENTS (expected_nr_band_class_array), ==, nr_band_class_array_size);
+    g_assert (memcmp (nr_band_class_array, expected_nr_band_class_array, nr_band_class_array_size * sizeof (guint16)) == 0);
+    g_assert_cmpstr (custom_data_class, ==, "HSPA+");
+    g_assert_cmpstr (device_id, ==, "862146050084555");
+    g_assert_cmpstr (firmware_info, ==, "81600.0000.99.29.17.19_GC\r\nB90");
+    g_assert_cmpstr (hardware_info, ==, "V1.0.6");
+}
+
+static void
+test_ms_basic_connect_extensions_wake_reason_command (void)
+{
+    g_autoptr(GError)       error = NULL;
+    g_autoptr(MbimMessage)  response = NULL;
+    gboolean                result;
+    MbimWakeType            wake_type;
+    guint32                 session_id;
+    g_autoptr(MbimTlv)      wake_tlv = NULL;
+    const MbimUuid         *service = NULL;
+    guint32                 cid = 0;
+    guint32                 payload_size = 0;
+    g_autofree guint8      *payload = NULL;
+
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x5C, 0x00, 0x00, 0x00, /* length */
+        0x04, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0x3D, 0x01, 0xDC, 0xC5, /* service id */
+        0xFE, 0xF5, 0x4D, 0x05,
+        0x0D, 0x3A, 0xBE, 0xF7,
+        0x05, 0x8E, 0x9A, 0xAF,
+        0x13, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x2C, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x01, 0x00, 0x00, 0x00, /* wake type: cid indication */
+        0x02, 0x00, 0x00, 0x00, /* session id */
+        /* TLV */
+        0x10, 0x00, 0x00, 0x00, /* TLV type MBIM_TLV_TYPE_WAKE_COMMAND, padding 0 */
+        0x1C, 0x00, 0x00, 0x00, /* TLV data length */
+        0xA2, 0x89, 0xCC, 0x33, /* service id: basic connect */
+        0xBC, 0xBB, 0x8B, 0x4F,
+        0xB6, 0xB0, 0x13, 0x3E,
+        0xC2, 0xAA, 0xE6, 0xDF,
+        0x0B, 0x00, 0x00, 0x00, /* command id: signal state */
+        0x00, 0x00, 0x00, 0x00, /* payload offset: none */
+        0x00, 0x00, 0x00, 0x00, /* payload size: none */
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 3, 0);
+
+    result = (mbim_message_ms_basic_connect_extensions_v3_wake_reason_response_parse (
+                  response,
+                  &wake_type,
+                  &session_id,
+                  &wake_tlv,
+                  &error));
+    g_assert_no_error (error);
+    g_assert (result);
+
+    g_assert_cmpuint (wake_type, ==, MBIM_WAKE_TYPE_CID_INDICATION);
+    g_assert_cmpuint (session_id, ==, 2);
+    g_assert_nonnull (wake_tlv);
+    g_assert_cmpuint (mbim_tlv_get_tlv_type (wake_tlv), ==, MBIM_TLV_TYPE_WAKE_COMMAND);
+
+    result = (mbim_tlv_wake_command_get (wake_tlv,
+                                         &service,
+                                         &cid,
+                                         &payload_size,
+                                         &payload,
+                                         &error));
+    g_assert_no_error (error);
+    g_assert (result);
+
+    g_assert_cmpuint (mbim_uuid_to_service (service), ==, MBIM_SERVICE_BASIC_CONNECT);
+    g_assert_cmpuint (cid, ==, MBIM_CID_BASIC_CONNECT_SIGNAL_STATE);
+    g_assert_cmpuint (payload_size, ==, 0);
+    g_assert_null (payload);
+}
+
+static void
+test_ms_basic_connect_extensions_wake_reason_command_payload (void)
+{
+    g_autoptr(GError)       error = NULL;
+    g_autoptr(MbimMessage)  response = NULL;
+    gboolean                result;
+    MbimWakeType            wake_type;
+    guint32                 session_id;
+    g_autoptr(MbimTlv)      wake_tlv = NULL;
+    const MbimUuid         *service = NULL;
+    guint32                 cid = 0;
+    guint32                 payload_size = 0;
+    g_autofree guint8      *payload = NULL;
+    guint32                 payload_uint;
+
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x60, 0x00, 0x00, 0x00, /* length */
+        0x04, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0x3D, 0x01, 0xDC, 0xC5, /* service id */
+        0xFE, 0xF5, 0x4D, 0x05,
+        0x0D, 0x3A, 0xBE, 0xF7,
+        0x05, 0x8E, 0x9A, 0xAF,
+        0x13, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x30, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x00, 0x00, 0x00, 0x00, /* wake type: cid response */
+        0x02, 0x00, 0x00, 0x00, /* session id */
+        /* TLV */
+        0x10, 0x00, 0x00, 0x00, /* TLV type MBIM_TLV_TYPE_WAKE_COMMAND, padding 0 */
+        0x20, 0x00, 0x00, 0x00, /* TLV data length */
+        0xA2, 0x89, 0xCC, 0x33, /* service id: basic connect */
+        0xBC, 0xBB, 0x8B, 0x4F,
+        0xB6, 0xB0, 0x13, 0x3E,
+        0xC2, 0xAA, 0xE6, 0xDF,
+        0x0C, 0x00, 0x00, 0x00, /* command id: connect */
+        0x1C, 0x00, 0x00, 0x00, /* payload offset: 28 */
+        0x04, 0x00, 0x00, 0x00, /* payload size: 4 */
+        0x01, 0x00, 0x00, 0x00, /* payload: a guint32 */
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 3, 0);
+
+    result = (mbim_message_ms_basic_connect_extensions_v3_wake_reason_response_parse (
+                  response,
+                  &wake_type,
+                  &session_id,
+                  &wake_tlv,
+                  &error));
+    g_assert_no_error (error);
+    g_assert (result);
+
+    g_assert_cmpuint (wake_type, ==, MBIM_WAKE_TYPE_CID_RESPONSE);
+    g_assert_cmpuint (session_id, ==, 2);
+    g_assert_nonnull (wake_tlv);
+    g_assert_cmpuint (mbim_tlv_get_tlv_type (wake_tlv), ==, MBIM_TLV_TYPE_WAKE_COMMAND);
+
+    result = (mbim_tlv_wake_command_get (wake_tlv,
+                                         &service,
+                                         &cid,
+                                         &payload_size,
+                                         &payload,
+                                         &error));
+    g_assert_no_error (error);
+    g_assert (result);
+
+    g_assert_cmpuint (mbim_uuid_to_service (service), ==, MBIM_SERVICE_BASIC_CONNECT);
+    g_assert_cmpuint (cid, ==, MBIM_CID_BASIC_CONNECT_CONNECT);
+    g_assert_cmpuint (payload_size, ==, 4);
+    g_assert_nonnull (payload);
+
+    memcpy (&payload_uint, payload, payload_size);
+    payload_uint = GUINT32_FROM_LE (payload_uint);
+    g_assert_cmpuint (payload_uint, ==, 1);
+}
+
+static void
+test_ms_basic_connect_extensions_wake_reason_packet (void)
+{
+    g_autoptr(GError)       error = NULL;
+    g_autoptr(MbimMessage)  response = NULL;
+    gboolean                result;
+    MbimWakeType            wake_type;
+    guint32                 session_id;
+    g_autoptr(MbimTlv)      wake_tlv = NULL;
+    guint32                 filter_id = 0;
+    guint32                 original_packet_size = 0;
+    guint32                 packet_size = 0;
+    g_autofree guint8      *packet = NULL;
+    const guint8            expected_packet[] = { 0x01, 0x02, 0x03, 0x04,
+                                                  0x05, 0x06, 0x07, 0x08,
+                                                  0x09, 0x0A };
+
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x5C, 0x00, 0x00, 0x00, /* length */
+        0x04, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0x3D, 0x01, 0xDC, 0xC5, /* service id */
+        0xFE, 0xF5, 0x4D, 0x05,
+        0x0D, 0x3A, 0xBE, 0xF7,
+        0x05, 0x8E, 0x9A, 0xAF,
+        0x13, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x2C, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x02, 0x00, 0x00, 0x00, /* wake type: packet */
+        0x02, 0x00, 0x00, 0x00, /* session id */
+        /* TLV */
+        0x11, 0x00, 0x00, 0x02, /* TLV type MBIM_TLV_TYPE_WAKE_PACKET, padding 2 */
+        0x1A, 0x00, 0x00, 0x00, /* TLV data length */
+        0x0B, 0x00, 0x00, 0x00, /* filter id */
+        0x0C, 0x00, 0x00, 0x00, /* original packet size: 12 */
+        0x10, 0x00, 0x00, 0x00, /* packet offset: 16 */
+        0x0A, 0x00, 0x00, 0x00, /* packet size: 10 */
+        0x01, 0x02, 0x03, 0x04,
+        0x05, 0x06, 0x07, 0x08,
+        0x09, 0x0A, 0x00, 0x00, /* last 2 bytes padding */
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+    test_message_printable (response, 3, 0);
+
+    result = (mbim_message_ms_basic_connect_extensions_v3_wake_reason_response_parse (
+                  response,
+                  &wake_type,
+                  &session_id,
+                  &wake_tlv,
+                  &error));
+    g_assert_no_error (error);
+    g_assert (result);
+
+    g_assert_cmpuint (wake_type, ==, MBIM_WAKE_TYPE_PACKET);
+    g_assert_cmpuint (session_id, ==, 2);
+    g_assert_nonnull (wake_tlv);
+    g_assert_cmpuint (mbim_tlv_get_tlv_type (wake_tlv), ==, MBIM_TLV_TYPE_WAKE_PACKET);
+
+    result = (mbim_tlv_wake_packet_get (wake_tlv,
+                                        &filter_id,
+                                        &original_packet_size,
+                                        &packet_size,
+                                        &packet,
+                                        &error));
+    g_assert_no_error (error);
+    g_assert (result);
+
+    g_assert_cmpuint (filter_id, ==, 0x0B);
+    g_assert_cmpuint (original_packet_size, ==, 12);
+    g_assert_cmpuint (packet_size, ==, sizeof (expected_packet));
+    g_assert_nonnull (packet);
+    g_assert (memcmp (packet, expected_packet, sizeof (expected_packet)) == 0);
+}
+
 int main (int argc, char **argv)
 {
     g_test_init (&argc, &argv, NULL);
 
-    g_test_add_func ("/libmbim-glib/message/parser/basic-connect/visible-providers", test_message_parser_basic_connect_visible_providers);
-    g_test_add_func ("/libmbim-glib/message/parser/basic-connect/subscriber-ready-status", test_message_parser_basic_connect_subscriber_ready_status);
-    g_test_add_func ("/libmbim-glib/message/parser/basic-connect/device-caps", test_message_parser_basic_connect_device_caps);
-    g_test_add_func ("/libmbim-glib/message/parser/basic-connect/ip-configuration", test_message_parser_basic_connect_ip_configuration);
-    g_test_add_func ("/libmbim-glib/message/parser/basic-connect/service-activation", test_message_parser_basic_connect_service_activation);
-    g_test_add_func ("/libmbim-glib/message/parser/basic-connect/register-state", test_message_parser_basic_connect_register_state);
-    g_test_add_func ("/libmbim-glib/message/parser/basic-connect/provisioned-contexts", test_message_parser_provisioned_contexts);
-    g_test_add_func ("/libmbim-glib/message/parser/sms/read/zero-pdu", test_message_parser_sms_read_zero_pdu);
-    g_test_add_func ("/libmbim-glib/message/parser/sms/read/single-pdu", test_message_parser_sms_read_single_pdu);
-    g_test_add_func ("/libmbim-glib/message/parser/sms/read/multiple-pdu", test_message_parser_sms_read_multiple_pdu);
-    g_test_add_func ("/libmbim-glib/message/parser/ussd", test_message_parser_ussd);
-    g_test_add_func ("/libmbim-glib/message/parser/auth/akap", test_message_parser_auth_akap);
-    g_test_add_func ("/libmbim-glib/message/parser/stk/pac/notification", test_message_parser_stk_pac_notification);
-    g_test_add_func ("/libmbim-glib/message/parser/stk/pac/response", test_message_parser_stk_pac_response);
-    g_test_add_func ("/libmbim-glib/message/parser/stk/terminal/response", test_message_parser_stk_terminal_response);
-    g_test_add_func ("/libmbim-glib/message/parser/stk/envelope/response", test_message_parser_stk_envelope_response);
-    g_test_add_func ("/libmbim-glib/message/parser/basic-connect/ip-packet-filters/none", test_message_parser_basic_connect_ip_packet_filters_none);
-    g_test_add_func ("/libmbim-glib/message/parser/basic-connect/ip-packet-filters/one", test_message_parser_basic_connect_ip_packet_filters_one);
-    g_test_add_func ("/libmbim-glib/message/parser/basic-connect/ip-packet-filters/two", test_message_parser_basic_connect_ip_packet_filters_two);
-    g_test_add_func ("/libmbim-glib/message/parser/ms-firmware-id/get", test_message_parser_ms_firmware_id_get);
-    g_test_add_func ("/libmbim-glib/message/parser/basic-connect/connect/short", test_message_parser_basic_connect_connect_short);
-    g_test_add_func ("/libmbim-glib/message/parser/basic-connect/visible-providers/overflow", test_message_parser_basic_connect_visible_providers_overflow);
+#define PREFIX "/libmbim-glib/message/parser"
+
+    g_test_add_func (PREFIX "/basic-connect/visible-providers", test_basic_connect_visible_providers);
+    g_test_add_func (PREFIX "/basic-connect/subscriber-ready-status", test_basic_connect_subscriber_ready_status);
+    g_test_add_func (PREFIX "/basic-connect/device-caps", test_basic_connect_device_caps);
+    g_test_add_func (PREFIX "/basic-connect/ip-configuration", test_basic_connect_ip_configuration);
+    g_test_add_func (PREFIX "/basic-connect/service-activation", test_basic_connect_service_activation);
+    g_test_add_func (PREFIX "/basic-connect/register-state", test_basic_connect_register_state);
+    g_test_add_func (PREFIX "/basic-connect/provisioned-contexts", test_provisioned_contexts);
+    g_test_add_func (PREFIX "/sms/read/zero-pdu", test_sms_read_zero_pdu);
+    g_test_add_func (PREFIX "/sms/read/single-pdu", test_sms_read_single_pdu);
+    g_test_add_func (PREFIX "/sms/read/multiple-pdu", test_sms_read_multiple_pdu);
+    g_test_add_func (PREFIX "/ussd", test_ussd);
+    g_test_add_func (PREFIX "/auth/akap", test_auth_akap);
+    g_test_add_func (PREFIX "/stk/pac/notification", test_stk_pac_notification);
+    g_test_add_func (PREFIX "/stk/pac/response", test_stk_pac_response);
+    g_test_add_func (PREFIX "/stk/terminal/response", test_stk_terminal_response);
+    g_test_add_func (PREFIX "/stk/envelope/response", test_stk_envelope_response);
+    g_test_add_func (PREFIX "/basic-connect/ip-packet-filters/none", test_basic_connect_ip_packet_filters_none);
+    g_test_add_func (PREFIX "/basic-connect/ip-packet-filters/one", test_basic_connect_ip_packet_filters_one);
+    g_test_add_func (PREFIX "/basic-connect/ip-packet-filters/two", test_basic_connect_ip_packet_filters_two);
+    g_test_add_func (PREFIX "/ms-firmware-id/get", test_ms_firmware_id_get);
+    g_test_add_func (PREFIX "/basic-connect/connect/short", test_basic_connect_connect_short);
+    g_test_add_func (PREFIX "/basic-connect/visible-providers/overflow", test_basic_connect_visible_providers_overflow);
+    g_test_add_func (PREFIX "/basic-connect-extensions/base-stations", test_ms_basic_connect_extensions_base_stations);
+    g_test_add_func (PREFIX "/basic-connect-extensions/registration-parameters/0-unnamed-tlvs", test_ms_basic_connect_extensions_registration_parameters_0_unnamed_tlvs);
+    g_test_add_func (PREFIX "/basic-connect-extensions/registration-parameters/1-unnamed-tlv", test_ms_basic_connect_extensions_registration_parameters_1_unnamed_tlv);
+    g_test_add_func (PREFIX "/basic-connect-extensions/registration-parameters/3-unnamed-tlvs", test_ms_basic_connect_extensions_registration_parameters_3_unnamed_tlvs);
+    g_test_add_func (PREFIX "/basic-connect-v3/connect/0-unnamed-tlvs", test_ms_basic_connect_v3_connect_0_unnamed_tlvs);
+    g_test_add_func (PREFIX "/basic-connect-v3/connect/1-unnamed-tlv", test_ms_basic_connect_v3_connect_1_unnamed_tlv);
+    g_test_add_func (PREFIX "/basic-connect-v3/connect/3-unnamed-tlvs", test_ms_basic_connect_v3_connect_3_unnamed_tlvs);
+    g_test_add_func (PREFIX "/basic-connect-extensions/device-caps-v3", test_ms_basic_connect_extensions_device_caps_v3);
+    g_test_add_func (PREFIX "/basic-connect-extensions/wake-reason/command", test_ms_basic_connect_extensions_wake_reason_command);
+    g_test_add_func (PREFIX "/basic-connect-extensions/wake-reason/command/payload", test_ms_basic_connect_extensions_wake_reason_command_payload);
+    g_test_add_func (PREFIX "/basic-connect-extensions/wake-reason/packet", test_ms_basic_connect_extensions_wake_reason_packet);
+
+#undef PREFIX
 
     return g_test_run ();
 }
diff --git a/src/mbim-proxy/meson.build b/src/mbim-proxy/meson.build
index 8c21c02..c316aa6 100644
--- a/src/mbim-proxy/meson.build
+++ b/src/mbim-proxy/meson.build
@@ -16,7 +16,7 @@
   configure_file(
     input: '76-mbim-proxy-device-ownership.rules.in',
     output: '@BASENAME@',
-    configuration: config_h,
-    install_dir: join_paths(udev_udevdir, 'rules.d'),
+    configuration: {'MBIM_USERNAME': mbim_username},
+    install_dir: udev_udevdir / 'rules.d',
   )
 endif
diff --git a/src/mbimcli/mbimcli-basic-connect.c b/src/mbimcli/mbimcli-basic-connect.c
index 0ac3c9a..50ca6d9 100644
--- a/src/mbimcli/mbimcli-basic-connect.c
+++ b/src/mbimcli/mbimcli-basic-connect.c
@@ -58,6 +58,7 @@
 static gchar    *set_connect_deactivate_str;
 static gboolean  query_packet_statistics_flag;
 static gchar    *query_ip_packet_filters_str;
+static gchar    *set_ip_packet_filters_str;
 static gboolean  query_provisioned_contexts_flag;
 
 static gboolean query_connection_state_arg_parse (const char *option_name,
@@ -172,7 +173,7 @@
       "[SessionID]"
     },
     { "connect", 0, 0, G_OPTION_ARG_STRING, &set_connect_activate_str,
-      "Connect (allowed keys: session-id, apn, ip-type (ipv4|ipv6|ipv4v6), auth (PAP|CHAP|MSCHAPV2), username, password)",
+      "Connect (allowed keys: session-id, access-string, ip-type, auth, username, password, compression, context-type)",
       "[\"key=value,...\"]"
     },
     { "query-ip-configuration", 0, G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, G_CALLBACK (query_ip_configuration_arg_parse),
@@ -191,6 +192,10 @@
       "Query IP packet filters (SessionID is optional, defaults to 0)",
       "[SessionID]"
     },
+    { "set-ip-packet-filters", 0, 0, G_OPTION_ARG_STRING, &set_ip_packet_filters_str,
+      "Set IP packet filters (allowed keys: session-id, packet-filter, packet-mask, filter-id)",
+      "[\"key=value,...\"]"
+    },
     { "query-provisioned-contexts", 0, 0, G_OPTION_ARG_NONE, &query_provisioned_contexts_flag,
       "Query provisioned contexts",
       NULL
@@ -289,6 +294,7 @@
                  !!set_connect_deactivate_str +
                  query_packet_statistics_flag +
                  !!query_ip_packet_filters_str +
+                 !!set_ip_packet_filters_str +
                  query_provisioned_contexts_flag);
 
     if (n_actions > 1) {
@@ -416,17 +422,18 @@
 query_subscriber_ready_status_ready (MbimDevice   *device,
                                      GAsyncResult *res)
 {
-    g_autoptr (MbimMessage)   response = NULL;
-    g_autoptr (GError)        error = NULL;
-    MbimSubscriberReadyState  ready_state;
-    const gchar              *ready_state_str;
-    g_autofree gchar         *subscriber_id = NULL;
-    g_autofree gchar         *sim_iccid = NULL;
-    MbimReadyInfoFlag         ready_info;
-    g_autofree gchar         *ready_info_str = NULL;
-    guint32                   telephone_numbers_count;
-    g_auto(GStrv)             telephone_numbers = NULL;
-    g_autofree gchar         *telephone_numbers_str = NULL;
+    g_autoptr (MbimMessage)        response = NULL;
+    g_autoptr (GError)             error = NULL;
+    MbimSubscriberReadyState       ready_state;
+    const gchar                   *ready_state_str;
+    g_autofree gchar              *subscriber_id = NULL;
+    g_autofree gchar              *sim_iccid = NULL;
+    MbimReadyInfoFlag              ready_info;
+    g_autofree gchar              *ready_info_str = NULL;
+    guint32                        telephone_numbers_count;
+    g_auto(GStrv)                  telephone_numbers = NULL;
+    g_autofree gchar              *telephone_numbers_str = NULL;
+    MbimSubscriberReadyStatusFlag  flags;
 
     response = mbim_device_command_finish (device, res, &error);
     if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
@@ -435,18 +442,40 @@
         return;
     }
 
-    if (!mbim_message_subscriber_ready_status_response_parse (
-            response,
-            &ready_state,
-            &subscriber_id,
-            &sim_iccid,
-            &ready_info,
-            &telephone_numbers_count,
-            &telephone_numbers,
-            &error)) {
-        g_printerr ("error: couldn't parse response message: %s\n", error->message);
-        shutdown (FALSE);
-        return;
+    /* MBIMEx 3.0 support */
+    if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+        if (!mbim_message_ms_basic_connect_v3_subscriber_ready_status_response_parse (response,
+                                                                                      &ready_state,
+                                                                                      &flags,
+                                                                                      &subscriber_id,
+                                                                                      &sim_iccid,
+                                                                                      &ready_info,
+                                                                                      &telephone_numbers_count,
+                                                                                      &telephone_numbers,
+                                                                                      &error)) {
+
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+        g_debug ("Successfully parsed response as MBIMEx 3.0 Subscriber State");
+    }
+    /* MBIM 1.0 support */
+    else {
+        if (!mbim_message_subscriber_ready_status_response_parse (response,
+                                                                  &ready_state,
+                                                                  &subscriber_id,
+                                                                  &sim_iccid,
+                                                                  &ready_info,
+                                                                  &telephone_numbers_count,
+                                                                  &telephone_numbers,
+                                                                  &error)) {
+
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+         }
+        g_debug ("Successfully parsed response as MBIM 1.0 Subscriber State");
     }
 
     telephone_numbers_str = (telephone_numbers ? g_strjoinv (", ", telephone_numbers) : NULL);
@@ -466,6 +495,14 @@
              VALIDATE_UNKNOWN (ready_info_str),
              telephone_numbers_count, VALIDATE_UNKNOWN (telephone_numbers_str));
 
+    if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+        g_autofree gchar *flags_str = NULL;
+
+        flags_str = mbim_subscriber_ready_status_flag_build_string_from_mask (flags);
+        g_print ("\t            Flags: '%s'\n",
+                 VALIDATE_UNKNOWN (flags_str));
+    }
+
     shutdown (TRUE);
 }
 
@@ -678,7 +715,13 @@
     }
 
     if (pin_type && (g_strv_length (split) == n_max)) {
-        new_pin_type = mbimcli_read_pintype_from_string (split[n++]);
+        const gchar *pin_type_str;
+
+        pin_type_str = split[n++];
+        if (!mbimcli_read_pin_type_from_string (pin_type_str, &new_pin_type)) {
+            g_printerr ("error: couldn't parse input pin-type: %s\n", pin_type_str);
+            return FALSE;
+        }
         if (new_pin_type == MBIM_PIN_TYPE_UNKNOWN ||
             (*pin_type == MBIM_PIN_TYPE_PIN1 && new_pin_type >= MBIM_PIN_TYPE_PUK1) ||
             (*pin_type == MBIM_PIN_TYPE_PUK1 && new_pin_type < MBIM_PIN_TYPE_PUK1)) {
@@ -843,14 +886,16 @@
                GAsyncResult *res,
                gpointer      user_data)
 {
-    g_autoptr(MbimMessage)  response = NULL;
-    g_autoptr(GError)       error = NULL;
-    guint32                 session_id;
-    MbimActivationState     activation_state;
-    MbimVoiceCallState      voice_call_state;
-    MbimContextIpType       ip_type;
-    const MbimUuid         *context_type;
-    guint32                 nw_error;
+    g_autoptr(MbimMessage)   response = NULL;
+    g_autoptr(GError)        error = NULL;
+    guint32                  session_id;
+    MbimActivationState      activation_state;
+    MbimVoiceCallState       voice_call_state;
+    MbimContextIpType        ip_type;
+    MbimAccessMediaType      media_type = MBIM_ACCESS_MEDIA_TYPE_UNKNOWN;
+    g_autofree gchar        *access_string = NULL;
+    const MbimUuid          *context_type;
+    guint32                  nw_error;
 
     response = mbim_device_command_finish (device, res, &error);
     if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
@@ -859,18 +904,37 @@
         return;
     }
 
-    if (!mbim_message_connect_response_parse (
-            response,
-            &session_id,
-            &activation_state,
-            &voice_call_state,
-            &ip_type,
-            &context_type,
-            &nw_error,
-            &error)) {
-        g_printerr ("error: couldn't parse response message: %s\n", error->message);
-        shutdown (FALSE);
-        return;
+    if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+        if (!mbim_message_ms_basic_connect_v3_connect_response_parse (
+                response,
+                &session_id,
+                &activation_state,
+                &voice_call_state,
+                &ip_type,
+                &context_type,
+                &nw_error,
+                &media_type,
+                &access_string,
+                NULL, /* unnamed IEs ignored */
+                &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+    } else {
+        if (!mbim_message_connect_response_parse (
+                response,
+                &session_id,
+                &activation_state,
+                &voice_call_state,
+                &ip_type,
+                &context_type,
+                &nw_error,
+                &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
     }
 
     switch (GPOINTER_TO_UINT (user_data)) {
@@ -901,6 +965,13 @@
              VALIDATE_UNKNOWN (mbim_context_type_get_string (mbim_uuid_to_context_type (context_type))),
              VALIDATE_UNKNOWN (mbim_nw_error_get_string (nw_error)));
 
+    if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+        g_print ("\tAccess media type: '%s'\n"
+                 "\t    Access string: '%s'\n",
+                 VALIDATE_UNKNOWN (mbim_access_media_type_get_string (media_type)),
+                 access_string);
+    }
+
     if (GPOINTER_TO_UINT (user_data) == CONNECT) {
         ip_configuration_query (device, NULL, session_id);
         return;
@@ -913,11 +984,12 @@
 ip_packet_filters_ready (MbimDevice   *device,
                          GAsyncResult *res)
 {
-    g_autoptr(MbimMessage)           response = NULL;
-    g_autoptr(GError)                error = NULL;
-    g_autoptr(MbimPacketFilterArray) filters = NULL;
-    guint32                          filters_count;
-    guint32                          i;
+    g_autoptr(MbimMessage)             response = NULL;
+    g_autoptr(GError)                  error = NULL;
+    g_autoptr(MbimPacketFilterArray)   filters = NULL;
+    g_autoptr(MbimPacketFilterV3Array) filters_v3 = NULL;
+    guint32                            filters_count;
+    guint32                            i;
 
     response = mbim_device_command_finish (device, res, &error);
     if (!response ||
@@ -927,76 +999,65 @@
         return;
     }
 
-    if (!mbim_message_ip_packet_filters_response_parse (
-            response,
-            NULL, /* sessionid */
-            &filters_count,
-            &filters,
-            &error)) {
-        g_printerr ("error: couldn't parse response message: %s\n", error->message);
-        shutdown (FALSE);
-        return;
+    /* MBIMEx 3.0 support */
+    if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+        if (!mbim_message_ms_basic_connect_v3_ip_packet_filters_response_parse (
+                response,
+                NULL, /* sessionid */
+                &filters_count,
+                &filters_v3,
+                &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+        g_debug ("Successfully parsed response as MBIMEx 3.0 IP Packet Filters");
+    }
+    /* MBIM 1.0 support */
+    else {
+        if (!mbim_message_ip_packet_filters_response_parse (
+                response,
+                NULL, /* sessionid */
+                &filters_count,
+                &filters,
+                &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+        g_debug ("Successfully parsed response as MBIM 1.0 IP Packet Filters");
     }
 
-    g_print ("\n[%s] IP packet filters: (%u)\n", mbim_device_get_path_display (device), filters_count);
+    g_print ("[%s] IP packet filters: (%u)\n", mbim_device_get_path_display (device), filters_count);
 
     for (i = 0; i < filters_count; i++) {
         g_autofree gchar *packet_filter = NULL;
-        g_autofree gchar *packet_mask = NULL;
+        g_autofree gchar *packet_mask   = NULL;
+        guint32           filter_size = 0;
 
-        packet_filter = mbim_common_str_hex (filters[i]->packet_filter, filters[i]->filter_size, ' ');
-        packet_mask   = mbim_common_str_hex (filters[i]->packet_mask, filters[i]->filter_size, ' ');
+        if (filters_v3) {
+            filter_size   = filters_v3[i]->filter_size;
+            packet_filter = mbim_common_str_hex (filters_v3[i]->packet_filter, filter_size, ' ');
+            packet_mask   = mbim_common_str_hex (filters_v3[i]->packet_mask, filter_size, ' ');
+        } else if (filters) {
+            filter_size   = filters[i]->filter_size;
+            packet_filter = mbim_common_str_hex (filters[i]->packet_filter, filter_size, ' ');
+            packet_mask   = mbim_common_str_hex (filters[i]->packet_mask, filter_size, ' ');
+        } else
+            g_assert_not_reached ();
 
-        g_print ("\n");
-        g_print ("\tFilter size: %u\n", filters[i]->filter_size);
-        g_print ("\tPacket filter: %s\n", VALIDATE_UNKNOWN (packet_filter));
-        g_print ("\tPacket mask: %s\n", VALIDATE_UNKNOWN (packet_mask));
+        g_print ("Filter %u:\n", i);
+        g_print ("\tFilter size   : %u\n", filter_size);
+        g_print ("\tPacket filter : %s\n", VALIDATE_UNKNOWN (packet_filter));
+        g_print ("\tPacket mask   : %s\n", VALIDATE_UNKNOWN (packet_mask));
+        if (filters_v3)
+            g_print ("\tFilter ID     : %u\n", filters_v3[i]->filter_id);
     }
 
     shutdown (TRUE);
 }
 
 static gboolean
-mbim_auth_protocol_from_string (const gchar      *str,
-                                MbimAuthProtocol *auth_protocol)
-{
-    if (g_ascii_strcasecmp (str, "PAP") == 0) {
-        *auth_protocol = MBIM_AUTH_PROTOCOL_PAP;
-        return TRUE;
-    }
-    if (g_ascii_strcasecmp (str, "CHAP") == 0) {
-        *auth_protocol = MBIM_AUTH_PROTOCOL_CHAP;
-        return TRUE;
-    }
-    if (g_ascii_strcasecmp (str, "MSCHAPV2") == 0) {
-        *auth_protocol = MBIM_AUTH_PROTOCOL_MSCHAPV2;
-        return TRUE;
-    }
-
-    return FALSE;
-}
-
-static gboolean
-mbim_context_ip_type_from_string (const gchar       *str,
-                                  MbimContextIpType *ip_type)
-{
-    if (g_ascii_strcasecmp (str, "ipv4") == 0) {
-        *ip_type = MBIM_CONTEXT_IP_TYPE_IPV4;
-        return TRUE;
-    }
-    if (g_ascii_strcasecmp (str, "ipv6") == 0) {
-        *ip_type = MBIM_CONTEXT_IP_TYPE_IPV6;
-        return TRUE;
-    }
-    if (g_ascii_strcasecmp (str, "ipv4v6") == 0) {
-        *ip_type = MBIM_CONTEXT_IP_TYPE_IPV4V6;
-        return TRUE;
-    }
-
-    return FALSE;
-}
-
-static gboolean
 connect_session_id_parse (const gchar  *str,
                           gboolean      allow_empty,
                           guint32      *session_id,
@@ -1036,18 +1097,209 @@
 }
 
 typedef struct {
-    guint32            session_id;
-    gchar             *apn;
-    MbimAuthProtocol   auth_protocol;
-    gchar             *username;
-    gchar             *password;
-    MbimContextIpType  ip_type;
+    gboolean   v3;
+    guint32    session_id;
+    GPtrArray *array;
+    gchar     *tmp_packet_filter;
+    gchar     *tmp_packet_mask;
+    gchar     *tmp_filter_id;
+} SetIpPacketFiltersProperties;
+
+static void
+mbim_packet_filter_v3_free (MbimPacketFilterV3 *var)
+{
+    if (!var)
+        return;
+
+    g_free (var->packet_filter);
+    g_free (var->packet_mask);
+    g_free (var);
+}
+
+static void
+mbim_packet_filter_free (MbimPacketFilter *var)
+{
+    if (!var)
+        return;
+
+    g_free (var->packet_filter);
+    g_free (var->packet_mask);
+    g_free (var);
+}
+
+static void
+set_ip_packet_filters_properties_clear (SetIpPacketFiltersProperties *props)
+{
+    g_free (props->tmp_packet_filter);
+    g_free (props->tmp_packet_mask);
+    g_free (props->tmp_filter_id);
+    g_ptr_array_unref (props->array);
+}
+
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(SetIpPacketFiltersProperties, set_ip_packet_filters_properties_clear);
+
+static gboolean
+check_filter_add (SetIpPacketFiltersProperties  *props,
+                  GError                       **error)
+{
+    g_autofree guint8 *packet_filter = NULL;
+    gsize              packet_filter_size = 0;
+    g_autofree guint8 *packet_mask = NULL;
+    gsize              packet_mask_size = 0;
+
+    if (!props->tmp_packet_filter) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED,
+                     "Option 'packet-filter' is missing");
+        return FALSE;
+    }
+
+    if (!props->tmp_packet_mask) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED,
+                     "Option 'packet-mask' is missing");
+        return FALSE;
+    }
+
+    if (!props->v3 && props->tmp_filter_id) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED,
+                     "Option 'filter-id' is specific to MBIMEx v3.0");
+        return FALSE;
+    }
+
+    if (props->v3 && !props->tmp_filter_id) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED,
+                     "Option 'filter-id' is missing");
+        return FALSE;
+    }
+
+    packet_filter = mbimcli_read_buffer_from_string (props->tmp_packet_filter, -1, &packet_filter_size, error);
+    if (!packet_filter)
+        return FALSE;
+
+    packet_mask = mbimcli_read_buffer_from_string (props->tmp_packet_mask, -1, &packet_mask_size, error);
+    if (!packet_mask)
+        return FALSE;
+
+    if (packet_filter_size != packet_mask_size) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED,
+                     "Option 'packet-filter' and 'packet-mask' must have same size");
+        return FALSE;
+    }
+
+    if (props->v3) {
+        MbimPacketFilterV3 *filter;
+        guint               filter_id;
+
+        if (!mbimcli_read_uint_from_string (props->tmp_filter_id, &filter_id)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED,
+                         "Failed to parse 'filter-id' field as an integer");
+            return FALSE;
+        }
+
+        filter = g_new0 (MbimPacketFilterV3, 1);
+        filter->filter_size = packet_filter_size;
+        filter->packet_filter = g_steal_pointer (&packet_filter);
+        filter->packet_mask = g_steal_pointer (&packet_mask);
+        filter->filter_id = filter_id;
+        g_ptr_array_add (props->array, filter);
+    } else {
+        MbimPacketFilter *filter;
+
+        filter = g_new0 (MbimPacketFilter, 1);
+        filter->filter_size = packet_filter_size;
+        filter->packet_filter = g_steal_pointer (&packet_filter);
+        filter->packet_mask = g_steal_pointer (&packet_mask);
+        g_ptr_array_add (props->array, filter);
+    }
+
+    g_clear_pointer (&props->tmp_filter_id, g_free);
+    g_clear_pointer (&props->tmp_packet_filter, g_free);
+    g_clear_pointer (&props->tmp_packet_mask, g_free);
+
+    return TRUE;
+}
+
+static gboolean
+set_ip_packet_filters_properties_handle (const gchar  *key,
+                                         const gchar  *value,
+                                         GError      **error,
+                                         gpointer      user_data)
+{
+    SetIpPacketFiltersProperties *props = user_data;
+
+    if (g_ascii_strcasecmp (key, "session-id") == 0) {
+        if (!connect_session_id_parse (value, FALSE, &props->session_id, error))
+            return FALSE;
+    } else if (g_ascii_strcasecmp (key, "packet-filter") == 0) {
+        if (props->tmp_packet_filter) {
+            if (!check_filter_add (props, error))
+                return FALSE;
+            g_clear_pointer (&props->tmp_packet_filter, g_free);
+        }
+        props->tmp_packet_filter = g_strdup (value);
+    } else if (g_ascii_strcasecmp (key, "packet-mask") == 0) {
+        if (props->tmp_packet_mask) {
+            if (!check_filter_add (props, error))
+                return FALSE;
+            g_clear_pointer (&props->tmp_packet_mask, g_free);
+        }
+        props->tmp_packet_mask = g_strdup (value);
+    } else if (g_ascii_strcasecmp (key, "filter-id") == 0) {
+        if (props->tmp_filter_id) {
+            if (!check_filter_add (props, error))
+                return FALSE;
+            g_clear_pointer (&props->tmp_filter_id, g_free);
+        }
+        props->tmp_filter_id = g_strdup (value);
+    } else {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED,
+                     "unrecognized option '%s'", key);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static gboolean
+set_ip_packet_filters_parse (const gchar                  *str,
+                             SetIpPacketFiltersProperties *props,
+                             MbimDevice                   *device)
+{
+    g_auto(GStrv)     split = NULL;
+    g_autoptr(GError) error = NULL;
+
+    if (!mbimcli_parse_key_value_string (str,
+                                         &error,
+                                         set_ip_packet_filters_properties_handle,
+                                         props)) {
+        g_printerr ("error: couldn't parse input string: %s\n", error->message);
+        return FALSE;
+    }
+
+    if ((props->tmp_packet_filter || props->tmp_packet_mask) &&
+        !check_filter_add (props, &error)) {
+        g_printerr ("error: failed to add last packet filter item: %s\n", error->message);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+typedef struct {
+    guint32             session_id;
+    gchar              *access_string;
+    MbimAuthProtocol    auth_protocol;
+    gchar              *username;
+    gchar              *password;
+    MbimContextIpType   ip_type;
+    MbimCompression     compression;
+    MbimContextType     context_type;
+    MbimAccessMediaType media_type;
 } ConnectActivateProperties;
 
 static void
 connect_activate_properties_clear (ConnectActivateProperties *props)
 {
-    g_free (props->apn);
+    g_free (props->access_string);
     g_free (props->username);
     g_free (props->password);
 }
@@ -1062,49 +1314,64 @@
 {
     ConnectActivateProperties *props = user_data;
 
-    /* APN may be empty */
-    if ((g_ascii_strcasecmp (key, "apn") != 0) && (!value || !value[0])) {
-        g_set_error (error,
-                     MBIM_CORE_ERROR,
-                     MBIM_CORE_ERROR_FAILED,
-                     "key '%s' required a value",
-                     key);
+    /* access-string/apn may be empty */
+    if ((g_ascii_strcasecmp (key, "access-string") != 0) &&
+        (g_ascii_strcasecmp (key, "apn") != 0) &&
+        (!value || !value[0])) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED,
+                     "key '%s' required a value", key);
         return FALSE;
     }
 
     if (g_ascii_strcasecmp (key, "session-id") == 0) {
         if (!connect_session_id_parse (value, FALSE, &props->session_id, error))
             return FALSE;
-    } else if (g_ascii_strcasecmp (key, "apn") == 0 && !props->apn) {
-        props->apn = g_strdup (value);
+    } else if (g_ascii_strcasecmp (key, "apn") == 0) {
+        g_printerr ("warning: key 'apn' is deprecated, use 'access-string' instead\n");
+        g_free (props->access_string);
+        props->access_string = g_strdup (value);
+    } else if (g_ascii_strcasecmp (key, "access-string") == 0) {
+        g_free (props->access_string);
+        props->access_string = g_strdup (value);
     } else if (g_ascii_strcasecmp (key, "auth") == 0) {
-        if (!mbim_auth_protocol_from_string (value, &props->auth_protocol)) {
-            g_set_error (error,
-                         MBIM_CORE_ERROR,
-                         MBIM_CORE_ERROR_FAILED,
-                         "unknown auth protocol '%s'",
-                         value);
+        if (!mbimcli_read_auth_protocol_from_string (value, &props->auth_protocol)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown auth: '%s'", value);
             return FALSE;
         }
-    } else if (g_ascii_strcasecmp (key, "username") == 0 && !props->username) {
+    } else if (g_ascii_strcasecmp (key, "username") == 0) {
+        g_free (props->username);
         props->username = g_strdup (value);
-    } else if (g_ascii_strcasecmp (key, "password") == 0 && !props->password) {
+    } else if (g_ascii_strcasecmp (key, "password") == 0) {
+        g_free (props->password);
         props->password = g_strdup (value);
     } else if (g_ascii_strcasecmp (key, "ip-type") == 0) {
-        if (!mbim_context_ip_type_from_string (value, &props->ip_type)) {
-            g_set_error (error,
-                         MBIM_CORE_ERROR,
-                         MBIM_CORE_ERROR_FAILED,
-                         "unknown ip type '%s'",
-                         value);
+        if (!mbimcli_read_context_ip_type_from_string (value, &props->ip_type)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown ip-type: '%s'", value);
+            return FALSE;
+        }
+    } else if (g_ascii_strcasecmp (key, "compression") == 0) {
+        if (!mbimcli_read_compression_from_string (value, &props->compression)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown compression: '%s'", value);
+            return FALSE;
+        }
+    } else if (g_ascii_strcasecmp (key, "context-type") == 0) {
+        if (!mbimcli_read_context_type_from_string (value, &props->context_type)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown context-type: '%s'", value);
+            return FALSE;
+        }
+    } else if (g_ascii_strcasecmp (key, "media-type") == 0) {
+        if (!mbimcli_read_access_media_type_from_string (value, &props->media_type)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown media-type: '%s'", value);
             return FALSE;
         }
     } else {
-            g_set_error (error,
-                         MBIM_CORE_ERROR,
-                         MBIM_CORE_ERROR_FAILED,
-                         "unrecognized or duplicate option '%s'",
-                         key);
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED,
+                     "unrecognized option '%s'", key);
         return FALSE;
     }
 
@@ -1112,32 +1379,13 @@
 }
 
 static gboolean
-set_connect_activate_parse (const gchar        *str,
-                            guint32            *session_id,
-                            gchar             **apn,
-                            MbimAuthProtocol   *auth_protocol,
-                            gchar             **username,
-                            gchar             **password,
-                            MbimContextIpType  *ip_type)
+set_connect_activate_parse (const gchar               *str,
+                            ConnectActivateProperties *props,
+                            MbimDevice                *device)
 {
-    g_auto(ConnectActivateProperties) props = {
-        .session_id    = 0,
-        .apn           = NULL,
-        .auth_protocol = MBIM_AUTH_PROTOCOL_NONE,
-        .username      = NULL,
-        .password      = NULL,
-        .ip_type       = MBIM_CONTEXT_IP_TYPE_DEFAULT
-    };
     g_auto(GStrv)     split = NULL;
     g_autoptr(GError) error = NULL;
 
-    g_assert (session_id != NULL);
-    g_assert (apn != NULL);
-    g_assert (auth_protocol != NULL);
-    g_assert (username != NULL);
-    g_assert (password != NULL);
-    g_assert (ip_type != NULL);
-
     if (strchr (str, '=')) {
         /* New key=value format */
         if (!mbimcli_parse_key_value_string (str,
@@ -1151,52 +1399,46 @@
         /* Old non key=value format, like this:
          *    "[(APN),(PAP|CHAP|MSCHAPV2),(Username),(Password)]"
          */
+        g_printerr ("warning: positional input arguments format is deprecated, use key-value format instead\n");
         split = g_strsplit (str, ",", -1);
 
         if (g_strv_length (split) > 4) {
             g_printerr ("error: couldn't parse input string, too many arguments\n");
             return FALSE;
-        }
+	    }
 
         if (g_strv_length (split) > 0) {
             /* APN */
-            props.apn = g_strdup (split[0]);
+            props->access_string = g_strdup (split[0]);
 
             /* Use authentication method */
             if (split[1]) {
-                if (!mbim_auth_protocol_from_string (split[1], &props.auth_protocol)) {
+                if (!mbimcli_read_auth_protocol_from_string (split[1], &props->auth_protocol)) {
                     g_printerr ("error: couldn't parse input string, unknown auth protocol '%s'\n", split[1]);
                     return FALSE;
                 }
                 /* Username */
                 if (split[2]) {
-                    props.username = g_strdup (split[2]);
+                    props->username = g_strdup (split[2]);
                     /* Password */
-                    props.password = g_strdup (split[3]);
+                    props->password = g_strdup (split[3]);
                 }
             }
         }
-    }
 
-    if (props.auth_protocol == MBIM_AUTH_PROTOCOL_NONE) {
-        if (props.username || props.password) {
-            g_printerr ("error: username or password requires an auth protocol\n");
-            return FALSE;
-        }
-    } else {
-        if (!props.username) {
-            g_printerr ("error: auth protocol requires a username\n");
-            return FALSE;
+        if (props->auth_protocol == MBIM_AUTH_PROTOCOL_NONE) {
+            if (props->username || props->password) {
+                g_printerr ("error: username or password requires an auth protocol\n");
+                return FALSE;
+            }
+        } else {
+            if (!props->username) {
+                g_printerr ("error: auth protocol requires a username\n");
+                return FALSE;
+            }
         }
     }
 
-    *session_id    = props.session_id;
-    *apn           = g_steal_pointer (&props.apn);
-    *auth_protocol = props.auth_protocol;
-    *username      = g_steal_pointer (&props.username);
-    *password      = g_steal_pointer (&props.password);
-    *ip_type       = props.ip_type;
-
     return TRUE;
 }
 
@@ -1371,6 +1613,7 @@
     g_autofree gchar       *roaming_text = NULL;
     MbimRegistrationFlag    registration_flag;
     g_autofree gchar       *registration_flag_str = NULL;
+    MbimDataClass           preferred_data_classes;
 
     response = mbim_device_command_finish (device, res, &error);
     if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
@@ -1379,20 +1622,44 @@
         return;
     }
 
-    if (!mbim_message_register_state_response_parse (response,
-                                                     &nw_error,
-                                                     &register_state,
-                                                     &register_mode,
-                                                     &available_data_classes,
-                                                     &cellular_class,
-                                                     &provider_id,
-                                                     &provider_name,
-                                                     &roaming_text,
-                                                     &registration_flag,
-                                                     &error)) {
-        g_printerr ("error: couldn't parse response message: %s\n", error->message);
-        shutdown (FALSE);
-        return;
+    /* MBIMEx 2.0 support */
+    if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+        if (!mbim_message_ms_basic_connect_v2_register_state_response_parse (response,
+                                                                             &nw_error,
+                                                                             &register_state,
+                                                                             &register_mode,
+                                                                             &available_data_classes,
+                                                                             &cellular_class,
+                                                                             &provider_id,
+                                                                             &provider_name,
+                                                                             &roaming_text,
+                                                                             &registration_flag,
+                                                                             &preferred_data_classes,
+                                                                             &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+        g_debug ("Successfully parsed response as MBIMEx 2.0 Register State");
+    }
+    /* MBIM 1.0 support */
+    else {
+        if (!mbim_message_register_state_response_parse (response,
+                                                         &nw_error,
+                                                         &register_state,
+                                                         &register_mode,
+                                                         &available_data_classes,
+                                                         &cellular_class,
+                                                         &provider_id,
+                                                         &provider_name,
+                                                         &roaming_text,
+                                                         &registration_flag,
+                                                         &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+        g_debug ("Successfully parsed response as MBIM 1.0 Register State");
     }
 
     if (GPOINTER_TO_UINT (user_data))
@@ -1424,6 +1691,14 @@
              VALIDATE_UNKNOWN (roaming_text),
              VALIDATE_UNKNOWN (registration_flag_str));
 
+    if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+        g_autofree gchar *preferred_data_classes_str = NULL;
+
+        preferred_data_classes_str = mbim_data_class_build_string_from_mask (preferred_data_classes);
+        g_print ("\tPreferred data classes: '%s'\n",
+                 VALIDATE_UNKNOWN (preferred_data_classes_str));
+    }
+
     shutdown (TRUE);
 }
 
@@ -1431,13 +1706,15 @@
 signal_state_ready (MbimDevice   *device,
                     GAsyncResult *res)
 {
-    g_autoptr(MbimMessage) response = NULL;
-    g_autoptr(GError)      error = NULL;
-    guint32                rssi;
-    guint32                error_rate;
-    guint32                signal_strength_interval;
-    guint32                rssi_threshold;
-    guint32                error_rate_threshold;
+    g_autoptr(MbimMessage)          response = NULL;
+    g_autoptr(GError)               error = NULL;
+    guint32                         rssi;
+    guint32                         error_rate;
+    guint32                         signal_strength_interval;
+    guint32                         rssi_threshold;
+    guint32                         error_rate_threshold;
+    guint32                         rsrp_snr_count;
+    g_autoptr(MbimRsrpSnrInfoArray) rsrp_snr = NULL;
 
     response = mbim_device_command_finish (device, res, &error);
     if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
@@ -1446,16 +1723,35 @@
         return;
     }
 
-    if (!mbim_message_signal_state_response_parse (response,
-                                                   &rssi,
-                                                   &error_rate,
-                                                   &signal_strength_interval,
-                                                   &rssi_threshold,
-                                                   &error_rate_threshold,
-                                                   &error)) {
-        g_printerr ("error: couldn't parse response message: %s\n", error->message);
-        shutdown (FALSE);
-        return;
+    /* MBIMEx 2.0 support */
+    if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+        if (!mbim_message_ms_basic_connect_v2_signal_state_response_parse (response,
+                                                                           &rssi,
+                                                                           &error_rate,
+                                                                           &signal_strength_interval,
+                                                                           &rssi_threshold,
+                                                                           &error_rate_threshold,
+                                                                           &rsrp_snr_count,
+                                                                           &rsrp_snr,
+                                                                           &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+    }
+    /* MBIM 1.0 support */
+    else {
+        if (!mbim_message_signal_state_response_parse (response,
+                                                       &rssi,
+                                                       &error_rate,
+                                                       &signal_strength_interval,
+                                                       &rssi_threshold,
+                                                       &error_rate_threshold,
+                                                       &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
     }
 
     g_print ("[%s] Signal state:\n"
@@ -1474,6 +1770,52 @@
     else
         g_print ("\t    Error rate threshold: '%u'\n", error_rate_threshold);
 
+    if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+        g_print ("\n");
+        if (rsrp_snr_count == 0) {
+            g_print ("[%s] RSRP/SNR info: 'n/a'\n", mbim_device_get_path_display (device));
+        } else {
+            guint i;
+
+            for (i = 0; i < rsrp_snr_count; i++) {
+                g_autofree gchar *system_type_str = NULL;
+                MbimRsrpSnrInfo  *info;
+
+                info = rsrp_snr[i];
+
+                system_type_str = mbim_data_class_build_string_from_mask (info->system_type);
+                g_print ("[%s] RSRP/SNR info: '%s'\n",
+                         mbim_device_get_path_display (device),
+                         system_type_str);
+
+                if (info->rsrp >= 127)
+                    g_print ("\t           RSRP: 'unknown'\n");
+                else
+                    g_print ("\t           RSRP: '%d dBm'\n", -157 + info->rsrp);
+
+                if (info->snr >= 128)
+                    g_print ("\t            SNR: 'unknown'\n");
+                else
+                    g_print ("\t            SNR: '%.1lf dB'\n", -23.5 + (info->snr * 0.5));
+
+                if (info->rsrp_threshold == 0)
+                    g_print ("\t RSRP threshold: 'default'\n");
+                else if (info->rsrp_threshold == 0xFFFFFFFF)
+                    g_print ("\t RSRP threshold: 'unspecified'\n");
+                else
+                    g_print ("\t RSRP threshold: '%u'\n", info->rsrp_threshold);
+
+                if (info->snr_threshold == 0)
+                    g_print ("\t  SNR threshold: 'default'\n");
+                else if (info->snr_threshold == 0xFFFFFFFF)
+                    g_print ("\t  SNR threshold: 'unspecified'\n");
+                else
+                    g_print ("\t  SNR threshold: '%u'\n", info->snr_threshold);
+                g_print ("\n");
+            }
+        }
+    }
+
     shutdown (TRUE);
 }
 
@@ -1493,9 +1835,13 @@
     guint32                 nw_error;
     MbimPacketServiceState  packet_service_state;
     MbimDataClass           highest_available_data_class;
+    MbimDataClassV3         highest_available_data_class_v3;
     g_autofree gchar       *highest_available_data_class_str = NULL;
     guint64                 uplink_speed;
     guint64                 downlink_speed;
+    MbimFrequencyRange      frequency_range;
+    MbimDataSubclass        data_subclass;
+    g_autoptr(MbimTai)      tai = NULL;
 
     response = mbim_device_command_finish (device, res, &error);
     if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
@@ -1504,16 +1850,54 @@
         return;
     }
 
-    if (!mbim_message_packet_service_response_parse (response,
-                                                     &nw_error,
-                                                     &packet_service_state,
-                                                     &highest_available_data_class,
-                                                     &uplink_speed,
-                                                     &downlink_speed,
-                                                     &error)) {
-        g_printerr ("error: couldn't parse response message: %s\n", error->message);
-        shutdown (FALSE);
-        return;
+    /* MBIMEx 3.0 support */
+    if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+        if (!mbim_message_ms_basic_connect_v3_packet_service_response_parse (response,
+                                                                             &nw_error,
+                                                                             &packet_service_state,
+                                                                             &highest_available_data_class_v3,
+                                                                             &uplink_speed,
+                                                                             &downlink_speed,
+                                                                             &frequency_range,
+                                                                             &data_subclass,
+                                                                             &tai,
+                                                                             &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+        g_debug ("Successfully parsed response as MBIM 3.0 Packet Service");
+    }
+    /* MBIMEx 2.0 support */
+    else if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+        if (!mbim_message_ms_basic_connect_v2_packet_service_response_parse (response,
+                                                                             &nw_error,
+                                                                             &packet_service_state,
+                                                                             &highest_available_data_class,
+                                                                             &uplink_speed,
+                                                                             &downlink_speed,
+                                                                             &frequency_range,
+                                                                             &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+        g_debug ("Successfully parsed response as MBIM 2.0 Packet Service");
+    }
+    /* MBIM 1.0 support */
+    else {
+        if (!mbim_message_packet_service_response_parse (response,
+                                                         &nw_error,
+                                                         &packet_service_state,
+                                                         &highest_available_data_class,
+                                                         &uplink_speed,
+                                                         &downlink_speed,
+                                                         &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+        g_debug ("Successfully parsed response as MBIM 1.0 Packet Service");
     }
 
     switch (GPOINTER_TO_UINT (user_data)) {
@@ -1529,7 +1913,10 @@
         break;
     }
 
-    highest_available_data_class_str = mbim_data_class_build_string_from_mask (highest_available_data_class);
+    if (mbim_device_check_ms_mbimex_version (device, 3, 0))
+        highest_available_data_class_str = mbim_data_class_v3_build_string_from_mask (highest_available_data_class_v3);
+    else
+        highest_available_data_class_str = mbim_data_class_build_string_from_mask (highest_available_data_class);
 
     g_print ("[%s] Packet service status:\n"
              "\t         Network error: '%s'\n"
@@ -1544,6 +1931,57 @@
              uplink_speed,
              downlink_speed);
 
+    if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+        g_autofree gchar *frequency_range_str = NULL;
+
+        frequency_range_str = mbim_frequency_range_build_string_from_mask (frequency_range);
+        g_print ("\t       Frequency range: '%s'\n",
+                 VALIDATE_UNKNOWN (frequency_range_str));
+    }
+
+    if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+        g_autofree gchar *data_subclass_str  = NULL;
+        g_autofree gchar *tai_plmn_mcc_str = NULL;
+        g_autofree gchar *tai_plmn_mnc_str = NULL;
+
+        /* Mobile Country Code of 3 decimal digits; The
+         * least significant 12 bits contains BCD-encoded
+         * 3 decimal digits sequentially for the MCC, with
+         * the last digit of the MCC in the least significant
+         * 4 bits. The unused bits in the UINT16 integer
+         * must be zeros. */
+        tai_plmn_mcc_str = g_strdup_printf ("%03x", tai->plmn_mcc & 0x0FFF);
+
+        /* Mobile Network Code of either 3 or 2 decimal
+         * digits; The most significant bit indicates
+         * whether the MNC has 2 decimal digits or 3
+         * decimal digits. If this bit has 1, the MNC has 2
+         * decimal digits and the least significant 8 bits
+         * contains them in BCD-encoded form
+         * sequentially, with the last digit of the MNC in
+         * the least significant 4 bits. If the most
+         * significant bit has 0, the MNC has 3 decimal
+         * digits and the least significant 12 bits contains
+         * them in BCD-encoded form sequentially, with
+         * the last digit of the MNC in the least
+         * esignificant 4 bits. The unused bits in the
+         * UINT16 integer must be zeros. */
+        if (tai->plmn_mnc & 0x8000)
+            tai_plmn_mnc_str = g_strdup_printf ("%02x", tai->plmn_mnc & 0x00FF);
+        else
+            tai_plmn_mnc_str = g_strdup_printf ("%03x", tai->plmn_mnc & 0x0FFF);
+
+        data_subclass_str = mbim_data_subclass_build_string_from_mask (data_subclass);
+        g_print ("\t        Data sub class: '%s'\n"
+                 "\t          TAI PLMN MCC: '%s'\n"
+                 "\t          TAI PLMN MNC: '%s'\n"
+                 "\t              TAI  TAC: '%u'\n",
+                 VALIDATE_UNKNOWN (data_subclass_str),
+                 tai_plmn_mcc_str,
+                 tai_plmn_mnc_str,
+                 tai->tac);
+    }
+
     shutdown (TRUE);
 }
 
@@ -1889,10 +2327,7 @@
 
     /* Launch automatic registration? */
     if (set_register_state_automatic_flag) {
-        request = mbim_message_register_state_set_new (NULL,
-                                                       MBIM_REGISTER_ACTION_AUTOMATIC,
-                                                       0,
-                                                       &error);
+        request = mbim_message_register_state_set_new (NULL, MBIM_REGISTER_ACTION_AUTOMATIC, 0, &error);
         if (!request) {
             g_printerr ("error: couldn't create request: %s\n", error->message);
             shutdown (FALSE);
@@ -1972,13 +2407,18 @@
             return;
         }
 
-        request = mbim_message_connect_query_new (session_id,
-                                                  MBIM_ACTIVATION_STATE_UNKNOWN,
-                                                  MBIM_VOICE_CALL_STATE_NONE,
-                                                  MBIM_CONTEXT_IP_TYPE_DEFAULT,
-                                                  mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
-                                                  0,
-                                                  &error);
+        if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+            request = mbim_message_ms_basic_connect_v3_connect_query_new (session_id,
+                                                                      &error);
+        } else {
+            request = mbim_message_connect_query_new (session_id,
+                                                      MBIM_ACTIVATION_STATE_UNKNOWN,
+                                                      MBIM_VOICE_CALL_STATE_NONE,
+                                                      MBIM_CONTEXT_IP_TYPE_DEFAULT,
+                                                      mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
+                                                      0,
+                                                      &error);
+        }
         if (!request) {
             g_printerr ("error: couldn't create request: %s\n", error->message);
             shutdown (FALSE);
@@ -1996,34 +2436,48 @@
 
     /* Connect? */
     if (set_connect_activate_str) {
-        guint32            session_id = 0;
-        g_autofree gchar  *apn = NULL;
-        MbimAuthProtocol   auth_protocol;
-        g_autofree gchar  *username = NULL;
-        g_autofree gchar  *password = NULL;
-        MbimContextIpType  ip_type = MBIM_CONTEXT_IP_TYPE_DEFAULT;
+        g_auto(ConnectActivateProperties) props = {
+            .session_id    = 0,
+            .access_string = NULL,
+            .auth_protocol = MBIM_AUTH_PROTOCOL_NONE,
+            .username      = NULL,
+            .password      = NULL,
+            .ip_type       = MBIM_CONTEXT_IP_TYPE_DEFAULT,
+            .compression   = MBIM_COMPRESSION_NONE,
+            .context_type  = MBIM_CONTEXT_TYPE_INTERNET,
+            .media_type    = MBIM_ACCESS_MEDIA_TYPE_UNKNOWN,
+        };
 
-        if (!set_connect_activate_parse (set_connect_activate_str,
-                                         &session_id,
-                                         &apn,
-                                         &auth_protocol,
-                                         &username,
-                                         &password,
-                                         &ip_type)) {
+        if (!set_connect_activate_parse (set_connect_activate_str, &props, device)) {
             shutdown (FALSE);
             return;
         }
 
-        request = mbim_message_connect_set_new (session_id,
-                                                MBIM_ACTIVATION_COMMAND_ACTIVATE,
-                                                apn,
-                                                username,
-                                                password,
-                                                MBIM_COMPRESSION_NONE,
-                                                auth_protocol,
-                                                ip_type,
-                                                mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
-                                                &error);
+        if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+            request = mbim_message_ms_basic_connect_v3_connect_set_new (props.session_id,
+                                                                        MBIM_ACTIVATION_COMMAND_ACTIVATE,
+                                                                        props.compression,
+                                                                        props.auth_protocol,
+                                                                        props.ip_type,
+                                                                        mbim_uuid_from_context_type (props.context_type),
+                                                                        props.media_type,
+                                                                        props.access_string,
+                                                                        props.username,
+                                                                        props.password,
+                                                                        NULL, /* unnamed IEs */
+                                                                        &error);
+        } else {
+            request = mbim_message_connect_set_new (props.session_id,
+                                                    MBIM_ACTIVATION_COMMAND_ACTIVATE,
+                                                    props.access_string,
+                                                    props.username,
+                                                    props.password,
+                                                    props.compression,
+                                                    props.auth_protocol,
+                                                    props.ip_type,
+                                                    mbim_uuid_from_context_type (props.context_type),
+                                                    &error);
+        }
 
         if (!request) {
             g_printerr ("error: couldn't create request: %s\n", error->message);
@@ -2064,16 +2518,32 @@
             return;
         }
 
-        request = mbim_message_connect_set_new (session_id,
-                                                MBIM_ACTIVATION_COMMAND_DEACTIVATE,
-                                                NULL,
-                                                NULL,
-                                                NULL,
-                                                MBIM_COMPRESSION_NONE,
-                                                MBIM_AUTH_PROTOCOL_NONE,
-                                                MBIM_CONTEXT_IP_TYPE_DEFAULT,
-                                                mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
-                                                &error);
+        if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+            request = mbim_message_ms_basic_connect_v3_connect_set_new (session_id,
+                                                                        MBIM_ACTIVATION_COMMAND_DEACTIVATE,
+                                                                        MBIM_COMPRESSION_NONE,
+                                                                        MBIM_AUTH_PROTOCOL_NONE,
+                                                                        MBIM_CONTEXT_IP_TYPE_DEFAULT,
+                                                                        mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
+                                                                        MBIM_ACCESS_MEDIA_TYPE_UNKNOWN,
+                                                                        NULL,
+                                                                        NULL,
+                                                                        NULL,
+                                                                        NULL,
+                                                                        &error);
+        } else {
+            request = mbim_message_connect_set_new (session_id,
+                                                    MBIM_ACTIVATION_COMMAND_DEACTIVATE,
+                                                    NULL,
+                                                    NULL,
+                                                    NULL,
+                                                    MBIM_COMPRESSION_NONE,
+                                                    MBIM_AUTH_PROTOCOL_NONE,
+                                                    MBIM_CONTEXT_IP_TYPE_DEFAULT,
+                                                    mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
+                                                    &error);
+        }
+
         if (!request) {
             g_printerr ("error: couldn't create request: %s\n", error->message);
             shutdown (FALSE);
@@ -2111,11 +2581,62 @@
             return;
         }
 
-        request = (mbim_message_ip_packet_filters_query_new (
-                       session_id,
-                       0, /* packet_filters_count */
-                       NULL, /* packet_filters */
-                       &error));
+        if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+            g_debug ("Asychronously querying v3.0 IP packet filter......");
+            request = mbim_message_ms_basic_connect_v3_ip_packet_filters_query_new (session_id, 0, NULL, &error);
+        } else {
+            g_debug ("Asychronously querying v1.0 IP packet filter......");
+            request = mbim_message_ip_packet_filters_query_new (session_id, 0, NULL, &error);
+        }
+
+        if (!request) {
+            g_printerr ("error: couldn't create IP packet filters request: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)ip_packet_filters_ready,
+                             NULL);
+        return;
+    }
+
+    /* Set IP packet filters? */
+    if (set_ip_packet_filters_str) {
+        g_auto(SetIpPacketFiltersProperties) props = {
+            .v3 = FALSE,
+            .session_id = 0,
+            .array = NULL,
+        };
+
+        props.v3 = mbim_device_check_ms_mbimex_version (device, 3, 0);
+        if (props.v3)
+            props.array = g_ptr_array_new_with_free_func ((GDestroyNotify)mbim_packet_filter_v3_free);
+        else
+            props.array = g_ptr_array_new_with_free_func ((GDestroyNotify)mbim_packet_filter_free);
+
+        if (!set_ip_packet_filters_parse (set_ip_packet_filters_str, &props, device)) {
+            shutdown (FALSE);
+            return;
+        }
+
+        if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+            g_debug ("Asychronously set v3.0 IP packet filter......");
+            request = mbim_message_ms_basic_connect_v3_ip_packet_filters_set_new (props.session_id,
+                                                                                  props.array->len,
+                                                                                  (const MbimPacketFilterV3 *const *)props.array->pdata,
+                                                                                  &error);
+        } else {
+            g_debug ("Asychronously set v1.0 IP packet filter......");
+            request = mbim_message_ip_packet_filters_set_new (props.session_id,
+                                                              props.array->len,
+                                                              (const MbimPacketFilter *const *)props.array->pdata,
+                                                              &error);
+        }
+
         if (!request) {
             g_printerr ("error: couldn't create IP packet filters request: %s\n", error->message);
             shutdown (FALSE);
diff --git a/src/mbimcli/mbimcli-helpers.c b/src/mbimcli/mbimcli-helpers.c
index dd19fe3..4025e53 100644
--- a/src/mbimcli/mbimcli-helpers.c
+++ b/src/mbimcli/mbimcli-helpers.c
@@ -37,6 +37,134 @@
 }
 
 gboolean
+mbimcli_read_uint_from_bcd_string (const gchar *str,
+                                   guint       *out)
+{
+    gulong num;
+
+    if (!str || !str[0])
+        return FALSE;
+
+    /* in bcd, only numeric values (0-9) */
+    for (num = 0; str[num]; num++) {
+        if (!g_ascii_isdigit (str[num]))
+            return FALSE;
+    }
+
+    /* for the numeric values of str, we can just read the string as hex
+     * (base 16) and it will be valid bcd */
+    errno = 0;
+    num = strtoul (str, NULL, 16);
+    if (!errno && num <= G_MAXUINT) {
+        *out = (guint)num;
+        return TRUE;
+    }
+    return FALSE;
+}
+
+gboolean
+mbimcli_read_uint8_from_bcd_string (const gchar *str,
+                                    guint8      *out)
+{
+    guint num;
+
+    if (!mbimcli_read_uint_from_bcd_string (str, &num) || (num > G_MAXUINT8))
+        return FALSE;
+
+    *out = (guint8)num;
+    return TRUE;
+}
+
+gboolean
+mbimcli_read_boolean_from_string (const gchar *value,
+                                  gboolean    *out)
+{
+    if (!g_ascii_strcasecmp (value, "true") || g_str_equal (value, "1") || !g_ascii_strcasecmp (value, "yes")) {
+        *out = TRUE;
+        return TRUE;
+    }
+
+    if (!g_ascii_strcasecmp (value, "false") || g_str_equal (value, "0") || !g_ascii_strcasecmp (value, "no")) {
+        *out = FALSE;
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+/* Based on ModemManager's mm_utils_hexstr2bin() */
+
+static gint
+hex2num (gchar c)
+{
+    if (c >= '0' && c <= '9')
+        return c - '0';
+    if (c >= 'a' && c <= 'f')
+        return c - 'a' + 10;
+    if (c >= 'A' && c <= 'F')
+        return c - 'A' + 10;
+    return -1;
+}
+
+static gint
+hex2byte (const gchar *hex)
+{
+    gint a, b;
+
+    a = hex2num (*hex++);
+    if (a < 0)
+        return -1;
+    b = hex2num (*hex++);
+    if (b < 0)
+        return -1;
+    return (a << 4) | b;
+}
+
+guint8 *
+mbimcli_read_buffer_from_string (const gchar  *hex,
+                                 gssize        len,
+                                 gsize        *out_len,
+                                 GError      **error)
+{
+    const gchar *ipos = hex;
+    g_autofree guint8 *buf = NULL;
+    gssize i;
+    gint a;
+    guint8 *opos;
+
+    if (len < 0)
+        len = strlen (hex);
+
+    if (len == 0) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED,
+                     "Hex conversion failed: empty string");
+        return NULL;
+    }
+
+    /* Length must be a multiple of 2 */
+    if ((len % 2) != 0) {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED,
+                     "Hex conversion failed: invalid input length");
+        return NULL;
+    }
+
+    opos = buf = g_malloc0 (len / 2);
+    for (i = 0; i < len; i += 2) {
+        a = hex2byte (ipos);
+        if (a < 0) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED,
+                         "Hex byte conversion from '%c%c' failed",
+                         ipos[0], ipos[1]);
+            return NULL;
+        }
+        *opos++ = (guint8)a;
+        ipos += 2;
+    }
+    *out_len = len / 2;
+    return g_steal_pointer (&buf);
+}
+
+gboolean
 mbimcli_print_ip_config (MbimDevice   *device,
                          MbimMessage  *response,
                          GError      **error)
@@ -319,22 +447,85 @@
     return TRUE;
 }
 
-MbimPinType
-mbimcli_read_pintype_from_string (const gchar *str)
+gboolean
+mbimcli_parse_sar_config_state_array (const gchar  *str,
+                                      GPtrArray   **out)
 {
-    const gchar *feature;
-    gint i;
+    g_autoptr(GPtrArray)  config_state_array = NULL;
+    g_autoptr(GRegex)     regex = NULL;
+    g_autoptr(GMatchInfo) match_info = NULL;
+    g_autoptr(GError)     inner_error = NULL;
 
-    if (str == NULL)
-        return MBIM_PIN_TYPE_UNKNOWN;
+    config_state_array = g_ptr_array_new_with_free_func (g_free);
 
-    /* Compare string to nicknames from mbim_pin_type_values */
-    i = MBIM_PIN_TYPE_CUSTOM;
-    while (NULL != (feature = mbim_pin_type_get_string (i))) {
-        if (g_str_equal (feature, str))
-            return i;
-        i++;
+    if (!str || !str[0]) {
+        *out = NULL;
+        return TRUE;
     }
 
-    return MBIM_PIN_TYPE_UNKNOWN;
+    regex = g_regex_new ("\\s*{\\s*(\\d+|all)\\s*,\\s*(\\d+)\\s*}(?:\\s*,)?", G_REGEX_RAW, 0, NULL);
+    g_assert (regex);
+
+    g_regex_match_full (regex, str, strlen (str), 0, 0, &match_info, &inner_error);
+    while (!inner_error && g_match_info_matches (match_info)) {
+        g_autofree MbimSarConfigState *config_state = NULL;
+        g_autofree gchar              *antenna_index_str = NULL;
+        g_autofree gchar              *backoff_index_str = NULL;
+
+        config_state = g_new (MbimSarConfigState, 1);
+
+        antenna_index_str = g_match_info_fetch (match_info, 1);
+        backoff_index_str = g_match_info_fetch (match_info, 2);
+
+        if (g_ascii_strcasecmp (antenna_index_str, "all") == 0)
+            config_state->antenna_index = 0xFFFFFFFF;
+        else if (!mbimcli_read_uint_from_string (antenna_index_str, &config_state->antenna_index)) {
+            g_printerr ("error: invalid antenna index: '%s'\n", antenna_index_str);
+            return FALSE;
+        }
+        if (!mbimcli_read_uint_from_string (backoff_index_str, &config_state->backoff_index)) {
+            g_printerr ("error: invalid backoff index: '%s'\n", backoff_index_str);
+            return FALSE;
+        }
+
+        g_ptr_array_add (config_state_array, g_steal_pointer (&config_state));
+        g_match_info_next (match_info, &inner_error);
+    }
+
+    if (inner_error) {
+        g_printerr ("error: couldn't match config state array: %s\n", inner_error->message);
+        return FALSE;
+    }
+
+    if (config_state_array->len == 0) {
+        g_printerr ("error: no elements found in the array\n");
+        return FALSE;
+    }
+
+    *out = (config_state_array->len > 0) ? g_steal_pointer (&config_state_array) : NULL;
+    return TRUE;
 }
+
+#define MBIMCLI_ENUM_LIST_ITEM(TYPE,TYPE_UNDERSCORE,DESCR)                    \
+    gboolean                                                                  \
+    mbimcli_read_## TYPE_UNDERSCORE ##_from_string (const gchar *str,         \
+                                                    TYPE *out)                \
+    {                                                                         \
+        GType type;                                                           \
+        GEnumClass *enum_class;                                               \
+        GEnumValue *enum_value;                                               \
+                                                                              \
+        type = mbim_## TYPE_UNDERSCORE ##_get_type ();                         \
+        enum_class = G_ENUM_CLASS (g_type_class_ref (type));                  \
+        enum_value = g_enum_get_value_by_nick (enum_class, str);              \
+                                                                              \
+        if (enum_value)                                                       \
+            *out = (TYPE)enum_value->value;                                   \
+        else                                                                  \
+            g_printerr ("error: invalid " DESCR " value given: '%s'\n", str); \
+                                                                              \
+        g_type_class_unref (enum_class);                                      \
+        return !!enum_value;                                                  \
+    }
+MBIMCLI_ENUM_LIST
+#undef MBIMCLI_ENUM_LIST_ITEM
diff --git a/src/mbimcli/mbimcli-helpers.h b/src/mbimcli/mbimcli-helpers.h
index 39e654d..cf99bb6 100644
--- a/src/mbimcli/mbimcli-helpers.h
+++ b/src/mbimcli/mbimcli-helpers.h
@@ -13,8 +13,20 @@
 #ifndef __MBIMCLI_HELPERS_H__
 #define __MBIMCLI_HELPERS_H__
 
-gboolean mbimcli_read_uint_from_string (const gchar *str,
-                                        guint *out);
+gboolean mbimcli_read_uint_from_string      (const gchar *str,
+                                             guint       *out);
+gboolean mbimcli_read_uint_from_bcd_string  (const gchar *str,
+                                             guint       *out);
+gboolean mbimcli_read_uint8_from_bcd_string (const gchar *str,
+                                             guint8      *out);
+
+gboolean mbimcli_read_boolean_from_string (const gchar *value,
+                                           gboolean    *out);
+
+guint8  *mbimcli_read_buffer_from_string (const gchar  *hex,
+                                          gssize        len,
+                                          gsize        *out_len,
+                                          GError      **error);
 
 gboolean mbimcli_print_ip_config (MbimDevice *device,
                                   MbimMessage *response,
@@ -30,6 +42,33 @@
                                          MbimParseKeyValueForeachFn callback,
                                          gpointer user_data);
 
-MbimPinType mbimcli_read_pintype_from_string (const gchar *str);
+gboolean mbimcli_parse_sar_config_state_array (const gchar  *str,
+                                               GPtrArray   **out);
+
+/* Common helpers to read enums from strings */
+
+#define MBIMCLI_ENUM_LIST                                                                                             \
+    MBIMCLI_ENUM_LIST_ITEM (MbimPinType,                  pin_type,                    "pin type")                    \
+    MBIMCLI_ENUM_LIST_ITEM (MbimContextType,              context_type,                "context type")                \
+    MBIMCLI_ENUM_LIST_ITEM (MbimContextIpType,            context_ip_type,             "context ip type")             \
+    MBIMCLI_ENUM_LIST_ITEM (MbimContextState,             context_state,               "context state")               \
+    MBIMCLI_ENUM_LIST_ITEM (MbimContextRoamingControl,    context_roaming_control,     "context roaming control")     \
+    MBIMCLI_ENUM_LIST_ITEM (MbimContextMediaType,         context_media_type,          "context media type")          \
+    MBIMCLI_ENUM_LIST_ITEM (MbimContextSource,            context_source,              "context source")              \
+    MBIMCLI_ENUM_LIST_ITEM (MbimContextOperation,         context_operation,           "context operation")           \
+    MBIMCLI_ENUM_LIST_ITEM (MbimAuthProtocol,             auth_protocol,               "auth protocol")               \
+    MBIMCLI_ENUM_LIST_ITEM (MbimCompression,              compression,                 "compression")                 \
+    MBIMCLI_ENUM_LIST_ITEM (MbimSarControlMode,           sar_control_mode,            "sar control mode")            \
+    MBIMCLI_ENUM_LIST_ITEM (MbimSarBackoffState,          sar_backoff_state,           "sar backoff state")           \
+    MBIMCLI_ENUM_LIST_ITEM (MbimMicoMode,                 mico_mode,                   "mico mode")                   \
+    MBIMCLI_ENUM_LIST_ITEM (MbimDrxCycle,                 drx_cycle,                   "drx cycle")                   \
+    MBIMCLI_ENUM_LIST_ITEM (MbimLadnInfo,                 ladn_info,                   "ladn info")                   \
+    MBIMCLI_ENUM_LIST_ITEM (MbimDefaultPduActivationHint, default_pdu_activation_hint, "default pdu activation hint") \
+    MBIMCLI_ENUM_LIST_ITEM (MbimAccessMediaType,          access_media_type,           "access media type")
+
+#define MBIMCLI_ENUM_LIST_ITEM(TYPE,TYPE_UNDERSCORE,DESCR)        \
+    gboolean mbimcli_read_## TYPE_UNDERSCORE ##_from_string (const gchar *str, TYPE *out);
+MBIMCLI_ENUM_LIST
+#undef MBIMCLI_ENUM_LIST_ITEM
 
 #endif /* __MBIMCLI_H__ */
diff --git a/src/mbimcli/mbimcli-ms-basic-connect-extensions.c b/src/mbimcli/mbimcli-ms-basic-connect-extensions.c
old mode 100755
new mode 100644
index 46a2cef..3149b70
--- a/src/mbimcli/mbimcli-ms-basic-connect-extensions.c
+++ b/src/mbimcli/mbimcli-ms-basic-connect-extensions.c
@@ -22,6 +22,7 @@
 
 #include "mbim-common.h"
 #include "mbimcli.h"
+#include "mbimcli-helpers.h"
 
 /* Context */
 typedef struct {
@@ -40,6 +41,15 @@
 static gchar    *query_slot_info_status_str;
 static gboolean  query_device_slot_mappings_flag;
 static gchar    *set_device_slot_mappings_str;
+static gboolean  query_location_info_status_flag;
+static gboolean  query_provisioned_contexts_flag;
+static gchar    *set_provisioned_contexts_str;
+static gboolean  query_base_stations_flag;
+static gchar    *query_version_str;
+static gboolean  query_registration_parameters_flag;
+static gchar    *set_registration_parameters_str;
+static gboolean  query_modem_configuration_flag;
+static gboolean  query_wake_reason_flag;
 
 static gboolean query_pco_arg_parse (const gchar  *option_name,
                                      const gchar  *value,
@@ -84,8 +94,43 @@
       "Query device slot mappings",
       NULL
     },
-
-    { NULL }
+    { "ms-query-location-info-status", 0, 0, G_OPTION_ARG_NONE, &query_location_info_status_flag,
+      "Query location info status",
+      NULL
+    },
+    { "ms-set-provisioned-contexts", 0, 0, G_OPTION_ARG_STRING, &set_provisioned_contexts_str,
+      "Set provisioned contexts (allowed keys: operation, context-type, ip-type, state, roaming-control, media-type, source, auth, compression, username, password, access-string)",
+      "[\"key=value,...\"]"
+    },
+    { "ms-query-provisioned-contexts", 0, 0, G_OPTION_ARG_NONE, &query_provisioned_contexts_flag,
+      "Query provisioned contexts",
+      NULL
+    },
+    { "ms-query-base-stations-info", 0, 0, G_OPTION_ARG_NONE, &query_base_stations_flag,
+      "Query base stations info",
+      NULL
+    },
+    { "ms-query-version", 0, 0,G_OPTION_ARG_STRING , &query_version_str,
+      "Exchange supported version information. Since MBIMEx v2.0.",
+      "[(MBIM version),(MBIM extended version)]"
+    },
+    { "ms-query-registration-parameters", 0, 0, G_OPTION_ARG_NONE, &query_registration_parameters_flag,
+      "Query registration parameters. Since MBIMEx v3.0.",
+      NULL
+    },
+    { "ms-set-registration-parameters", 0, 0,G_OPTION_ARG_STRING , &set_registration_parameters_str,
+      "Set registration parameters (required keys: mico-mode, drx-cycle, ladn-info, default-pdu-activation-hint, re-register-if-needed). Since MBIMEx v3.0.",
+      "[\"key=value,...\"]"
+    },
+    { "ms-query-modem-configuration", 0, 0, G_OPTION_ARG_NONE, &query_modem_configuration_flag,
+      "Query modem configuration. Since MBIMEx v3.0.",
+      NULL
+    },
+    { "ms-query-wake-reason", 0, 0, G_OPTION_ARG_NONE, &query_wake_reason_flag,
+      "Query wake reason. Since MBIMEx v3.0.",
+      NULL
+    },
+    {NULL }
 };
 
 static gboolean
@@ -160,7 +205,16 @@
                  query_device_caps_flag +
                  !!query_slot_info_status_str +
                  !!set_device_slot_mappings_str +
-                 query_device_slot_mappings_flag);
+                 query_device_slot_mappings_flag +
+                 query_location_info_status_flag +
+                 query_provisioned_contexts_flag +
+                 !!set_provisioned_contexts_str +
+                 query_base_stations_flag +
+                 !!query_version_str +
+                 query_registration_parameters_flag +
+                 !!set_registration_parameters_str +
+                 query_modem_configuration_flag +
+                 query_wake_reason_flag);
 
     if (n_actions > 1) {
         g_printerr ("error: too many Microsoft Basic Connect Extensions Service actions requested\n");
@@ -294,6 +348,7 @@
     g_autofree gchar       *password = NULL;
     guint32                 compression;
     guint32                 auth_protocol;
+    MbimNwError             nw_error = 0;
 
     response = mbim_device_command_finish (device, res, &error);
     if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
@@ -302,22 +357,45 @@
         return;
     }
 
-    g_print ("[%s] Successfully queried LTE attach info\n",
-             mbim_device_get_path_display (device));
 
-    if (!mbim_message_ms_basic_connect_extensions_lte_attach_info_response_parse (
-            response,
-            &lte_attach_state,
-            &ip_type,
-            &access_string,
-            &user_name,
-            &password,
-            &compression,
-            &auth_protocol,
-            &error)) {
-        g_printerr ("error: couldn't parse response message: %s\n", error->message);
-        shutdown (FALSE);
-        return;
+    /* MBIMEx 3.0 support */
+    if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+        if (!mbim_message_ms_basic_connect_extensions_v3_lte_attach_info_response_parse (
+                response,
+                &lte_attach_state,
+                &nw_error,
+                &ip_type,
+                &access_string,
+                &user_name,
+                &password,
+                &compression,
+                &auth_protocol,
+                &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+        g_print ("[%s] Successfully received v3.0 LTE attach info\n",
+                 mbim_device_get_path_display (device));
+    }
+    /* MBIM 1.0 support */
+    else {
+        if (!mbim_message_ms_basic_connect_extensions_lte_attach_info_response_parse (
+                response,
+                &lte_attach_state,
+                &ip_type,
+                &access_string,
+                &user_name,
+                &password,
+                &compression,
+                &auth_protocol,
+                &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+        g_print ("[%s] Successfully received v1.0 LTE attach info\n",
+                 mbim_device_get_path_display (device));
     }
 
 #define VALIDATE_NA(str) (str ? str : "n/a")
@@ -328,6 +406,21 @@
     g_print ("  Password:      %s\n", VALIDATE_NA (password));
     g_print ("  Compression:   %s\n", mbim_compression_get_string (compression));
     g_print ("  Auth protocol: %s\n", mbim_auth_protocol_get_string (auth_protocol));
+    if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+        if (nw_error == 0)
+            g_print ("  Network error: none\n");
+        else if (nw_error == 0xFFFFFFFF)
+            g_print ("  Network error: unknown\n");
+        else {
+            const gchar *nw_error_str;
+
+            nw_error_str = mbim_nw_error_get_string (nw_error);
+            if (nw_error_str)
+                g_print ("  Network error: %s\n", nw_error_str);
+            else
+                g_print ("  Network error: unknown (0x%08x)\n", nw_error);
+        }
+    }
 #undef VALIDATE_NA
 
     shutdown (TRUE);
@@ -394,13 +487,20 @@
     g_autofree gchar       *cellular_class_str = NULL;
     MbimSimClass            sim_class;
     g_autofree gchar       *sim_class_str = NULL;
-    MbimDataClass           data_class;
+    MbimDataClass           data_class = 0;
+    MbimDataClassV3         data_class_v3 = 0;
     g_autofree gchar       *data_class_str = NULL;
+    MbimDataSubclass        data_subclass;
     MbimSmsCaps             sms_caps;
     g_autofree gchar       *sms_caps_str = NULL;
     MbimCtrlCaps            ctrl_caps;
     g_autofree gchar       *ctrl_caps_str = NULL;
     guint32                 max_sessions;
+    guint32                 wcdma_band_class = 0;
+    guint32                 lte_band_class_array_size = 0;
+    g_autofree guint16     *lte_band_class_array = NULL;
+    guint32                 nr_band_class_array_size = 0;
+    g_autofree guint16     *nr_band_class_array = NULL;
     g_autofree gchar       *custom_data_class = NULL;
     g_autofree gchar       *device_id = NULL;
     g_autofree gchar       *firmware_info = NULL;
@@ -414,32 +514,67 @@
         return;
     }
 
-    if (!mbim_message_ms_basic_connect_extensions_device_caps_response_parse (
-            response,
-            &device_type,
-            &cellular_class,
-            &voice_class,
-            &sim_class,
-            &data_class,
-            &sms_caps,
-            &ctrl_caps,
-            &max_sessions,
-            &custom_data_class,
-            &device_id,
-            &firmware_info,
-            &hardware_info,
-            &executor_index,
-            &error)) {
-        g_printerr ("error: couldn't parse response message: %s\n", error->message);
-        shutdown (FALSE);
-        return;
+    if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+        if (!mbim_message_ms_basic_connect_extensions_v3_device_caps_response_parse (
+                response,
+                &device_type,
+                &cellular_class,
+                &voice_class,
+                &sim_class,
+                &data_class_v3,
+                &sms_caps,
+                &ctrl_caps,
+                &data_subclass,
+                &max_sessions,
+                &executor_index,
+                &wcdma_band_class,
+                &lte_band_class_array_size,
+                &lte_band_class_array,
+                &nr_band_class_array_size,
+                &nr_band_class_array,
+                &custom_data_class,
+                &device_id,
+                &firmware_info,
+                &hardware_info,
+                &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+        g_debug ("Successfully parsed response as MBIMEx 3.0 Device Caps");
+    } else {
+        if (!mbim_message_ms_basic_connect_extensions_device_caps_response_parse (
+                response,
+                &device_type,
+                &cellular_class,
+                &voice_class,
+                &sim_class,
+                &data_class,
+                &sms_caps,
+                &ctrl_caps,
+                &max_sessions,
+                &custom_data_class,
+                &device_id,
+                &firmware_info,
+                &hardware_info,
+                &executor_index,
+                &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+        g_debug ("Successfully parsed response as MBIMEx 1.0 Device Caps");
     }
 
+    if (mbim_device_check_ms_mbimex_version (device, 3, 0))
+        data_class_str = mbim_data_class_v3_build_string_from_mask (data_class_v3);
+    else
+        data_class_str = mbim_data_class_build_string_from_mask (data_class);
+
     device_type_str    = mbim_device_type_get_string (device_type);
     cellular_class_str = mbim_cellular_class_build_string_from_mask (cellular_class);
     voice_class_str    = mbim_voice_class_get_string (voice_class);
     sim_class_str      = mbim_sim_class_build_string_from_mask (sim_class);
-    data_class_str     = mbim_data_class_build_string_from_mask (data_class);
     sms_caps_str       = mbim_sms_caps_build_string_from_mask (sms_caps);
     ctrl_caps_str      = mbim_ctrl_caps_build_string_from_mask (ctrl_caps);
 
@@ -472,6 +607,34 @@
              VALIDATE_UNKNOWN (hardware_info),
              executor_index);
 
+    if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+        g_autofree gchar *data_subclass_str = NULL;
+        guint             i;
+        gboolean          n_printed;
+
+        data_subclass_str = mbim_data_subclass_build_string_from_mask (data_subclass);
+        g_print ("\t    Data subclass: '%s'\n", data_subclass_str);
+
+        g_print ("\t WCDMA band class: '");
+        for (n_printed = 0, i = 0; i < 31; i++) {
+            if (wcdma_band_class & (1 << i)) {
+                g_print ("%s%u", n_printed > 0 ? ", " : "", i + 1);
+                n_printed++;
+            }
+        }
+        g_print ("'\n");
+
+        g_print ("\t   LTE band class: '");
+        for (i = 0; i < lte_band_class_array_size; i++)
+            g_print ("%s%" G_GUINT16_FORMAT, i > 0 ? ", " : "", lte_band_class_array[i]);
+        g_print ("'\n");
+
+        g_print ("\t    NR band class: '");
+        for (i = 0; i < nr_band_class_array_size; i++)
+            g_print ("%s%" G_GUINT16_FORMAT, i > 0 ? ", " : "", nr_band_class_array[i]);
+        g_print ("'\n");
+    }
+
     shutdown (TRUE);
 }
 
@@ -635,6 +798,859 @@
     shutdown (TRUE);
 }
 
+static void
+query_location_info_status_ready (MbimDevice   *device,
+                                  GAsyncResult *res)
+{
+    g_autoptr(MbimMessage) response = NULL;
+    g_autoptr(GError)      error = NULL;
+    guint32                location_area_code = 0;
+    guint32                tracking_area_code = 0;
+    guint32                cell_id = 0;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        shutdown (FALSE);
+        return;
+    }
+
+    g_print ("[%s] Successfully queried location info status\n",
+             mbim_device_get_path_display (device));
+
+    if (!mbim_message_ms_basic_connect_extensions_location_info_status_response_parse (
+            response,
+            &location_area_code,
+            &tracking_area_code,
+            &cell_id,
+            &error)) {
+        g_printerr ("error: couldn't parse response message: %s\n", error->message);
+        shutdown (FALSE);
+        return;
+    }
+
+    g_print (" Location area code:  %04X\n", location_area_code);
+    g_print (" Tracking area code:  %06X\n", tracking_area_code);
+    g_print (" Cell ID:             %08X\n", cell_id);
+
+    shutdown (TRUE);
+}
+
+typedef struct {
+    MbimContextOperation       operation;
+    MbimContextIpType          ip_type;
+    MbimContextState           state;
+    MbimContextRoamingControl  roaming_control;
+    MbimContextMediaType       media_type;
+    MbimContextSource          source;
+    gchar                     *access_string;
+    gchar                     *username;
+    gchar                     *password;
+    MbimCompression            compression;
+    MbimAuthProtocol           auth_protocol;
+    MbimContextType            context_type;
+} ProvisionedContextProperties;
+
+static void
+provisioned_context_properties_clear (ProvisionedContextProperties *props)
+{
+    g_free (props->access_string);
+    g_free (props->username);
+    g_free (props->password);
+}
+
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(ProvisionedContextProperties, provisioned_context_properties_clear)
+
+static gboolean
+set_provisioned_contexts_foreach_cb (const gchar                   *key,
+                                     const gchar                   *value,
+                                     GError                       **error,
+                                     ProvisionedContextProperties  *props)
+{
+    if (g_ascii_strcasecmp (key, "operation") == 0) {
+        if (!mbimcli_read_context_operation_from_string (value, &props->operation)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown operation: '%s'", value);
+            return FALSE;
+        }
+    } else if (g_ascii_strcasecmp (key, "context-type") == 0) {
+        if (!mbimcli_read_context_type_from_string (value, &props->context_type)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown context-type: '%s'", value);
+            return FALSE;
+        }
+    } else if (g_ascii_strcasecmp (key, "ip-type") == 0) {
+        if (!mbimcli_read_context_ip_type_from_string (value, &props->ip_type)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown ip-type: '%s'", value);
+            return FALSE;
+        }
+    } else if (g_ascii_strcasecmp (key, "state") == 0) {
+        if (!mbimcli_read_context_state_from_string (value, &props->state)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown state: '%s'", value);
+            return FALSE;
+        }
+    } else if (g_ascii_strcasecmp (key, "roaming-control") == 0) {
+        if (!mbimcli_read_context_roaming_control_from_string (value, &props->roaming_control)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown roaming-control: '%s'", value);
+            return FALSE;
+        }
+    } else if (g_ascii_strcasecmp (key, "media-type") == 0) {
+        if (!mbimcli_read_context_media_type_from_string (value, &props->media_type)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown media-type: '%s'", value);
+            return FALSE;
+        }
+    } else if (g_ascii_strcasecmp (key, "source") == 0) {
+        if (!mbimcli_read_context_source_from_string (value, &props->source)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown source: '%s'", value);
+            return FALSE;
+        }
+    } else if (g_ascii_strcasecmp (key, "auth") == 0) {
+        if (!mbimcli_read_auth_protocol_from_string (value, &props->auth_protocol)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown auth: '%s'", value);
+            return FALSE;
+        }
+    } else if (g_ascii_strcasecmp (key, "compression") == 0) {
+        if (!mbimcli_read_compression_from_string (value, &props->compression)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown compression: '%s'", value);
+            return FALSE;
+        }
+    } else if (g_ascii_strcasecmp (key, "username") == 0) {
+        g_free (props->username);
+        props->username = g_strdup (value);
+    } else if (g_ascii_strcasecmp (key, "password") == 0) {
+        g_free (props->password);
+        props->password = g_strdup (value);
+    } else if (g_ascii_strcasecmp (key, "access-string") == 0) {
+        g_free (props->access_string);
+        props->access_string = g_strdup (value);
+    } else {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED,
+                     "unrecognized option '%s'", key);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+provisioned_contexts_ready (MbimDevice   *device,
+                            GAsyncResult *res)
+{
+    g_autoptr(MbimMessage)                          response = NULL;
+    g_autoptr(MbimProvisionedContextElementV2Array) provisioned_contexts = NULL;
+    g_autoptr(GError)                               error = NULL;
+    guint32 provisioned_contexts_count;
+    guint32 i = 0;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        shutdown (FALSE);
+        return;
+    }
+
+    if (!mbim_message_ms_basic_connect_extensions_provisioned_contexts_response_parse (
+            response,
+            &provisioned_contexts_count,
+            &provisioned_contexts,
+            &error)) {
+        g_printerr ("error: couldn't parse response message: %s\n", error->message);
+        shutdown (FALSE);
+        return;
+    }
+
+    g_print ("[%s] Provisioned contexts (%u):\n",
+             mbim_device_get_path_display (device),
+             provisioned_contexts_count);
+
+    for (i = 0; i < provisioned_contexts_count; i++) {
+        g_print ("\tContext ID %u:\n"
+                 "\t   Context type: '%s'\n"
+                 "\t        IP type: '%s'\n"
+                 "\t          State: '%s'\n"
+                 "\tRoaming control: '%s'\n"
+                 "\t     Media type: '%s'\n"
+                 "\t         Source: '%s'\n"
+                 "\t  Access string: '%s'\n"
+                 "\t       Username: '%s'\n"
+                 "\t       Password: '%s'\n"
+                 "\t    Compression: '%s'\n"
+                 "\t  Auth protocol: '%s'\n",
+                 provisioned_contexts[i]->context_id,
+                 VALIDATE_UNKNOWN (mbim_context_type_get_string (
+                     mbim_uuid_to_context_type (&provisioned_contexts[i]->context_type))),
+                 VALIDATE_UNKNOWN (mbim_context_ip_type_get_string (
+                     provisioned_contexts[i]->ip_type)),
+                 VALIDATE_UNKNOWN (mbim_context_state_get_string (
+                     provisioned_contexts[i]->state)),
+                 VALIDATE_UNKNOWN (mbim_context_roaming_control_get_string (
+                     provisioned_contexts[i]->roaming)),
+                 VALIDATE_UNKNOWN (mbim_context_media_type_get_string (
+                     provisioned_contexts[i]->media_type)),
+                 VALIDATE_UNKNOWN (mbim_context_source_get_string (
+                     provisioned_contexts[i]->source)),
+                 VALIDATE_UNKNOWN (provisioned_contexts[i]->access_string),
+                 VALIDATE_UNKNOWN (provisioned_contexts[i]->user_name),
+                 VALIDATE_UNKNOWN (provisioned_contexts[i]->password),
+                 VALIDATE_UNKNOWN (mbim_compression_get_string (
+                     provisioned_contexts[i]->compression)),
+                 VALIDATE_UNKNOWN (mbim_auth_protocol_get_string (
+                     provisioned_contexts[i]->auth_protocol)));
+    }
+
+    shutdown (TRUE);
+}
+
+static void
+query_base_stations_ready (MbimDevice   *device,
+                           GAsyncResult *res)
+{
+    g_autoptr(MbimMessage)                         response = NULL;
+    g_autoptr(GError)                              error = NULL;
+    MbimDataClass                                  system_type;
+    MbimDataClassV3                                system_type_v3;
+    MbimDataSubclass                               system_subtype;
+    g_autoptr(MbimCellInfoServingGsm)              gsm_serving_cell = NULL;
+    g_autoptr(MbimCellInfoServingUmts)             umts_serving_cell = NULL;
+    g_autoptr(MbimCellInfoServingTdscdma)          tdscdma_serving_cell = NULL;
+    g_autoptr(MbimCellInfoServingLte)              lte_serving_cell = NULL;
+    guint32                                        gsm_neighboring_cells_count;
+    g_autoptr(MbimCellInfoNeighboringGsmArray)     gsm_neighboring_cells = NULL;
+    guint32                                        umts_neighboring_cells_count;
+    g_autoptr(MbimCellInfoNeighboringUmtsArray)    umts_neighboring_cells = NULL;
+    guint32                                        tdscdma_neighboring_cells_count;
+    g_autoptr(MbimCellInfoNeighboringTdscdmaArray) tdscdma_neighboring_cells = NULL;
+    guint32                                        lte_neighboring_cells_count;
+    g_autoptr(MbimCellInfoNeighboringLteArray)     lte_neighboring_cells = NULL;
+    guint32                                        cdma_cells_count;
+    g_autoptr(MbimCellInfoCdmaArray)               cdma_cells = NULL;
+    guint32                                        nr_serving_cells_count;
+    g_autoptr(MbimCellInfoServingNrArray)          nr_serving_cells = NULL;
+    guint32                                        nr_neighboring_cells_count;
+    g_autoptr(MbimCellInfoNeighboringNrArray)      nr_neighboring_cells = NULL;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        shutdown (FALSE);
+        return;
+    }
+
+    /* MBIMEx 3.0 support */
+    if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+        if (!mbim_message_ms_basic_connect_extensions_v3_base_stations_info_response_parse (
+                response,
+                &system_type_v3,
+                &system_subtype,
+                &gsm_serving_cell,
+                &umts_serving_cell,
+                &tdscdma_serving_cell,
+                &lte_serving_cell,
+                &gsm_neighboring_cells_count,
+                &gsm_neighboring_cells,
+                &umts_neighboring_cells_count,
+                &umts_neighboring_cells,
+                &tdscdma_neighboring_cells_count,
+                &tdscdma_neighboring_cells,
+                &lte_neighboring_cells_count,
+                &lte_neighboring_cells,
+                &cdma_cells_count,
+                &cdma_cells,
+                &nr_serving_cells_count,
+                &nr_serving_cells,
+                &nr_neighboring_cells_count,
+                &nr_neighboring_cells,
+                &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+        g_debug ("Successfully parsed response as MBIMEx 3.0 Base Stations Info");
+    }
+    /* MBIMEx 1.0 support */
+    else {
+        if (!mbim_message_ms_basic_connect_extensions_base_stations_info_response_parse (
+                response,
+                &system_type,
+                &gsm_serving_cell,
+                &umts_serving_cell,
+                &tdscdma_serving_cell,
+                &lte_serving_cell,
+                &gsm_neighboring_cells_count,
+                &gsm_neighboring_cells,
+                &umts_neighboring_cells_count,
+                &umts_neighboring_cells,
+                &tdscdma_neighboring_cells_count,
+                &tdscdma_neighboring_cells,
+                &lte_neighboring_cells_count,
+                &lte_neighboring_cells,
+                &cdma_cells_count,
+                &cdma_cells,
+                &error)) {
+            g_printerr ("error: couldn't parse response message: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+        g_debug ("Successfully parsed response as MBIMEx 1.0 Base Stations Info");
+    }
+
+#define PRINT_VALIDATED_UINT(number, invalid, format, units) do {       \
+        if (number == invalid)                                          \
+            g_print ("%s: unknown\n", format);                          \
+        else                                                            \
+            g_print ("%s: %u%s\n", format, number, units ? units : ""); \
+    } while (0)
+
+#define PRINT_VALIDATED_UINT64(number, invalid, format, units) do {     \
+        if (number == invalid)                                          \
+            g_print ("%s: unknown\n", format);                          \
+        else                                                            \
+            g_print ("%s: %" G_GUINT64_FORMAT "%s\n", format, number, units ? units : ""); \
+    } while (0)
+
+#define PRINT_VALIDATED_INT(number, invalid, format, units) do {        \
+        if (number == (gint32)invalid)                                  \
+            g_print ("%s: unknown\n", format);                          \
+        else                                                            \
+            g_print ("%s: %d%s\n", format, number, units ? units : ""); \
+    } while (0)
+
+#define PRINT_VALIDATED_SCALED_UINT(number, invalid, scale, format, units) do {           \
+        if (number == 0xFFFFFFFF)                                                         \
+            g_print ("%s: unknown\n", format);                                            \
+        else                                                                              \
+            g_print ("%s: %d%s\n", format, ((gint32)number) + scale, units ? units : ""); \
+    } while (0)
+
+    if (mbim_device_check_ms_mbimex_version (device, 3, 0)) {
+        g_autofree gchar *system_type_str = NULL;
+        g_autofree gchar *system_subtype_str = NULL;
+
+        system_type_str = mbim_data_class_v3_build_string_from_mask (system_type_v3);
+        g_print ("System type: %s\n", system_type_str);
+        system_subtype_str = mbim_data_subclass_build_string_from_mask (system_subtype);
+        g_print ("System subtype: %s\n", VALIDATE_UNKNOWN (system_subtype_str));
+    } else {
+        g_autofree gchar *system_type_str = NULL;
+
+        system_type_str = mbim_data_class_build_string_from_mask (system_type);
+        g_print ("System type: %s\n", system_type_str);
+    }
+
+    if (gsm_serving_cell) {
+        g_print ("GSM serving cell:\n"
+                 "\t    Provider id: %s\n",
+                 VALIDATE_UNKNOWN (gsm_serving_cell->provider_id));
+        PRINT_VALIDATED_UINT        (gsm_serving_cell->location_area_code, 0xFFFFFFFF,       "\t            LAC", NULL);
+        PRINT_VALIDATED_UINT        (gsm_serving_cell->cell_id,            0xFFFFFFFF,       "\t        Cell ID", NULL);
+        PRINT_VALIDATED_UINT        (gsm_serving_cell->timing_advance,     0xFFFFFFFF,       "\t Timing advance", " bit periods");
+        PRINT_VALIDATED_UINT        (gsm_serving_cell->arfcn,              0xFFFFFFFF,       "\t          ARFCN", NULL);
+        PRINT_VALIDATED_UINT        (gsm_serving_cell->base_station_id,    0xFFFFFFFF,       "\tBase station ID", NULL);
+        PRINT_VALIDATED_SCALED_UINT (gsm_serving_cell->rx_level,           0xFFFFFFFF, -110, "\t       Rx level", " dBm");
+    } else
+        g_print ("GSM serving cell: n/a\n");
+
+    if (gsm_neighboring_cells_count && gsm_neighboring_cells) {
+        guint i;
+
+        g_print ("Neighboring GSM cells: %d\n", gsm_neighboring_cells_count);
+        for (i = 0; i < gsm_neighboring_cells_count; i++) {
+            g_print ("\tNeighboring cell [%u]:\n"
+                     "\t\t    Provider id: %s\n",
+                     i + 1,
+                     VALIDATE_UNKNOWN (gsm_neighboring_cells[i]->provider_id));
+            PRINT_VALIDATED_UINT        (gsm_neighboring_cells[i]->location_area_code, 0xFFFFFFFF,       "\t\t            LAC", NULL);
+            PRINT_VALIDATED_UINT        (gsm_neighboring_cells[i]->cell_id,            0xFFFFFFFF,       "\t\t        Cell ID", NULL);
+            PRINT_VALIDATED_UINT        (gsm_neighboring_cells[i]->arfcn,              0xFFFFFFFF,       "\t\t          ARFCN", NULL);
+            PRINT_VALIDATED_UINT        (gsm_neighboring_cells[i]->base_station_id,    0xFFFFFFFF,       "\t\tBase station ID", NULL);
+            PRINT_VALIDATED_SCALED_UINT (gsm_neighboring_cells[i]->rx_level,           0xFFFFFFFF, -110, "\t\t       Rx level", " dBm");
+        }
+    } else
+        g_print ("Neighboring GSM cells: n/a\n");
+
+    if (umts_serving_cell) {
+        g_print ("UMTS Serving cell:\n"
+                 "\t            Provider id: %s\n",
+                 VALIDATE_UNKNOWN (umts_serving_cell->provider_id));
+        PRINT_VALIDATED_UINT (umts_serving_cell->location_area_code,      0xFFFFFFFF, "\t                    LAC", NULL);
+        PRINT_VALIDATED_UINT (umts_serving_cell->cell_id,                 0xFFFFFFFF, "\t                Cell id", NULL);
+        PRINT_VALIDATED_UINT (umts_serving_cell->frequency_info_ul,       0xFFFFFFFF, "\t  Frequency info uplink", NULL);
+        PRINT_VALIDATED_UINT (umts_serving_cell->frequency_info_dl,       0xFFFFFFFF, "\tFrequency info downlink", NULL);
+        PRINT_VALIDATED_UINT (umts_serving_cell->frequency_info_nt,       0xFFFFFFFF, "\t     Frequency info TDD", NULL);
+        PRINT_VALIDATED_UINT (umts_serving_cell->uarfcn,                  0xFFFFFFFF, "\t                 UARFCN", NULL);
+        PRINT_VALIDATED_UINT (umts_serving_cell->primary_scrambling_code, 0xFFFFFFFF, "\tPrimary Scrambling Code", NULL);
+        /* note: docs say unknown value in this case is 0
+         * https://github.com/MicrosoftDocs/windows-driver-docs/issues/2932 */
+        PRINT_VALIDATED_INT  (umts_serving_cell->rscp,                    0xFFFFFFFF, "\t                   RSCP", " dBm");
+        PRINT_VALIDATED_INT  (umts_serving_cell->ecno,                    1,          "\t                   ECNO", " dBm");
+        PRINT_VALIDATED_UINT (umts_serving_cell->path_loss,               0xFFFFFFFF, "\t              Path loss", NULL);
+    } else
+        g_print ("UMTS serving cell: n/a\n");
+
+    if (umts_neighboring_cells_count && umts_neighboring_cells) {
+        guint i;
+
+        g_print ("Neighboring UMTS cells: %d\n", umts_neighboring_cells_count);
+        for (i = 0; i < umts_neighboring_cells_count; i++) {
+            g_print ("\tNeighboring cell [%u]:\n"
+                     "\t\t            Provider id: %s\n",
+                     i + 1,
+                     VALIDATE_UNKNOWN (umts_neighboring_cells[i]->provider_id));
+            PRINT_VALIDATED_UINT (umts_neighboring_cells[i]->location_area_code,      0xFFFFFFFF, "\t\t                    LAC", NULL);
+            PRINT_VALIDATED_UINT (umts_neighboring_cells[i]->cell_id,                 0xFFFFFFFF, "\t\t                Cell id", NULL);
+            PRINT_VALIDATED_UINT (umts_neighboring_cells[i]->uarfcn,                  0xFFFFFFFF, "\t\t                 UARFCN", NULL);
+            PRINT_VALIDATED_UINT (umts_neighboring_cells[i]->primary_scrambling_code, 0xFFFFFFFF, "\t\tPrimary Scrambling Code", NULL);
+            PRINT_VALIDATED_INT  (umts_neighboring_cells[i]->rscp,                    0xFFFFFFFF, "\t\t                   RSCP", " dBm");
+            PRINT_VALIDATED_INT  (umts_neighboring_cells[i]->ecno,                    1,          "\t\t                   ECNO", " dBm");
+            PRINT_VALIDATED_UINT (umts_neighboring_cells[i]->path_loss,               0xFFFFFFFF, "\t\t              Path loss", NULL);
+        }
+    } else
+        g_print ("Neighboring UMTS cells: n/a\n");
+
+    if (tdscdma_serving_cell) {
+        g_print ("TDSCDMA Serving cell:\n"
+                 "\t      Provider id: %s\n",
+                 VALIDATE_UNKNOWN (tdscdma_serving_cell->provider_id));
+        PRINT_VALIDATED_UINT (tdscdma_serving_cell->location_area_code, 0xFFFFFFFF, "\t              LAC", NULL);
+        PRINT_VALIDATED_UINT (tdscdma_serving_cell->cell_id,            0xFFFFFFFF, "\t          Cell id", NULL);
+        PRINT_VALIDATED_UINT (tdscdma_serving_cell->uarfcn,             0xFFFFFFFF, "\t           UARFCN", NULL);
+        PRINT_VALIDATED_UINT (tdscdma_serving_cell->cell_parameter_id,  0xFFFFFFFF, "\tCell parameter id", NULL);
+        PRINT_VALIDATED_UINT (tdscdma_serving_cell->timing_advance,     0xFFFFFFFF, "\t   Timing advance", NULL);
+        PRINT_VALIDATED_INT  (tdscdma_serving_cell->rscp,               0xFFFFFFFF, "\t             RSCP", " dBm");
+        PRINT_VALIDATED_UINT (tdscdma_serving_cell->path_loss,          0xFFFFFFFF, "\t        Path loss", NULL);
+    } else
+        g_print ("TDSCDMA serving cell: n/a\n");
+
+    if (tdscdma_neighboring_cells_count && tdscdma_neighboring_cells) {
+        guint i;
+
+        g_print ("Neighboring TDSCDMA cells: %d\n", tdscdma_neighboring_cells_count);
+        for (i = 0; i < tdscdma_neighboring_cells_count; i++) {
+            g_print ("\tNeighboring cell [%u]:\n"
+                     "\t\t      Provider id: %s\n",
+                     i + 1,
+                     VALIDATE_UNKNOWN (tdscdma_neighboring_cells[i]->provider_id));
+            PRINT_VALIDATED_UINT (tdscdma_neighboring_cells[i]->location_area_code, 0xFFFFFFFF, "\t\t              LAC", NULL);
+            PRINT_VALIDATED_UINT (tdscdma_neighboring_cells[i]->cell_id,            0xFFFFFFFF, "\t\t          Cell id", NULL);
+            PRINT_VALIDATED_UINT (tdscdma_neighboring_cells[i]->uarfcn,             0xFFFFFFFF, "\t\t           UARFCN", NULL);
+            PRINT_VALIDATED_UINT (tdscdma_neighboring_cells[i]->cell_parameter_id,  0xFFFFFFFF, "\t\tCell parameter id", NULL);
+            PRINT_VALIDATED_UINT (tdscdma_neighboring_cells[i]->timing_advance,     0xFFFFFFFF, "\t\t   Timing advance", NULL);
+            PRINT_VALIDATED_INT  (tdscdma_neighboring_cells[i]->rscp,               0xFFFFFFFF, "\t\t             RSCP", " dBm");
+            PRINT_VALIDATED_UINT (tdscdma_neighboring_cells[i]->path_loss,          0xFFFFFFFF, "\t\t        Path Loss", NULL);
+        }
+    } else
+        g_print ("Neighboring TDSCDMA cells: n/a\n");
+
+    if (lte_serving_cell) {
+        g_print ("LTE Serving cell:\n"
+                 "\t      Provider id: %s\n",
+                 VALIDATE_UNKNOWN (lte_serving_cell->provider_id));
+        PRINT_VALIDATED_UINT (lte_serving_cell->cell_id,          0xFFFFFFFF, "\t          Cell id", NULL);
+        PRINT_VALIDATED_UINT (lte_serving_cell->earfcn,           0xFFFFFFFF, "\t           EARFCN", NULL);
+        PRINT_VALIDATED_UINT (lte_serving_cell->physical_cell_id, 0xFFFFFFFF, "\t Physical cell id", NULL);
+        PRINT_VALIDATED_UINT (lte_serving_cell->tac,              0xFFFFFFFF, "\t              TAC", NULL);
+        PRINT_VALIDATED_INT  (lte_serving_cell->rsrp,             0xFFFFFFFF, "\t             RSRP", " dBm");
+        PRINT_VALIDATED_INT  (lte_serving_cell->rsrq,             0xFFFFFFFF, "\t             RSRQ", " dBm");
+        PRINT_VALIDATED_UINT (lte_serving_cell->timing_advance,   0xFFFFFFFF, "\t   Timing advance", NULL);
+    } else
+        g_print ("LTE serving cell: n/a\n");
+
+    if (lte_neighboring_cells_count && lte_neighboring_cells) {
+        guint i;
+
+        g_print ("Neighboring LTE cells: %d\n", lte_neighboring_cells_count);
+        for (i = 0; i < lte_neighboring_cells_count; i++) {
+            g_print ("\tNeighboring cell [%u]:\n"
+                     "\t\t      Provider id: %s\n",
+                     i + 1,
+                     VALIDATE_UNKNOWN (lte_neighboring_cells[i]->provider_id));
+            PRINT_VALIDATED_UINT (lte_neighboring_cells[i]->cell_id,          0xFFFFFFFF, "\t\t          Cell id", NULL);
+            PRINT_VALIDATED_UINT (lte_neighboring_cells[i]->earfcn,           0xFFFFFFFF, "\t\t           EARFCN", NULL);
+            PRINT_VALIDATED_UINT (lte_neighboring_cells[i]->physical_cell_id, 0xFFFFFFFF, "\t\t Physical cell id", NULL);
+            PRINT_VALIDATED_UINT (lte_neighboring_cells[i]->tac,              0xFFFFFFFF, "\t\t              TAC", NULL);
+            PRINT_VALIDATED_INT  (lte_neighboring_cells[i]->rsrp,             0xFFFFFFFF, "\t\t             RSRP", " dBm");
+            PRINT_VALIDATED_INT  (lte_neighboring_cells[i]->rsrq,             0xFFFFFFFF, "\t\t             RSRQ", " dBm");
+        }
+    } else
+        g_print ("Neighboring LTE cells: n/a\n");
+
+    if (cdma_cells_count && cdma_cells) {
+        guint i;
+
+        g_print ("CDMA cells: %d\n", cdma_cells_count);
+        for (i = 0; i < cdma_cells_count; i++)
+            g_print ("Cell [%u]:\n"
+                     "\t        Serving: %s\n",
+                     i + 1,
+                     cdma_cells[i]->serving_cell_flag ? "yes" : "no");
+        PRINT_VALIDATED_UINT (cdma_cells[i]->nid,             0xFFFFFFFF, "\t            NID", NULL);
+        PRINT_VALIDATED_UINT (cdma_cells[i]->sid,             0xFFFFFFFF, "\t            SID", NULL);
+        PRINT_VALIDATED_UINT (cdma_cells[i]->base_station_id, 0xFFFFFFFF, "\tBase station id", NULL);
+        /* TODO: The Base Station Latitude (0-4194303). This is encoded in units of 0.25 seconds, expressed
+         * in two’s complement representation within the low 22 bits of the DWORD. As a signed value,
+         * North latitudes are positive. Use 0xFFFFFFFF when this information is not available. */
+        PRINT_VALIDATED_UINT (cdma_cells[i]->base_latitude,   0xFFFFFFFF, "\t  Base latitude", NULL);
+        /* TODO: The Base Station Longitude (0-8388607). This is encoded in units of 0.25 seconds, expressed
+         * in two’s complement representation within the low 23 bits of the DWORD. As a signed value, East
+         * longitudes are positive. Use 0xFFFFFFFF when this information is not available. */
+        PRINT_VALIDATED_UINT (cdma_cells[i]->base_longitude,  0xFFFFFFFF, "\t Base longitude", NULL);
+        PRINT_VALIDATED_UINT (cdma_cells[i]->ref_pn,          0xFFFFFFFF, "\t          RefPN", NULL);
+        PRINT_VALIDATED_UINT (cdma_cells[i]->gps_seconds,     0xFFFFFFFF, "\t    GPS seconds", " seconds");
+        PRINT_VALIDATED_UINT (cdma_cells[i]->pilot_strength,  0xFFFFFFFF, "\t Pilot strength", NULL);
+    } else
+        g_print ("CDMA cells: n/a\n");
+
+    if (nr_serving_cells_count && nr_serving_cells) {
+        guint i;
+
+        g_print ("Serving NR cells: %d\n", nr_serving_cells_count);
+        for (i = 0; i < nr_serving_cells_count; i++) {
+            g_print ("\tServing cell [%u]:\n"
+                     "\t\t     Provider id: %s\n",
+                     i + 1,
+                     VALIDATE_UNKNOWN (nr_serving_cells[i]->provider_id));
+            PRINT_VALIDATED_UINT64      (nr_serving_cells[i]->nci,              0xFFFFFFFFFFFFFFFF, "\t\t             NCI", NULL);
+            PRINT_VALIDATED_UINT        (nr_serving_cells[i]->physical_cell_id, 0xFFFFFFFF,         "\t\tPhysical cell id", NULL);
+            PRINT_VALIDATED_UINT        (nr_serving_cells[i]->nrarfcn,          0xFFFFFFFF,         "\t\t         NRARFCN", NULL);
+            PRINT_VALIDATED_UINT        (nr_serving_cells[i]->tac,              0xFFFFFFFF,         "\t\t             TAC", NULL);
+            PRINT_VALIDATED_SCALED_UINT (nr_serving_cells[i]->rsrp,             0xFFFFFFFF, -156,   "\t\t            RSRP", " dBm");
+            PRINT_VALIDATED_SCALED_UINT (nr_serving_cells[i]->rsrq,             0xFFFFFFFF, -43,    "\t\t            RSRQ", " dB");
+            PRINT_VALIDATED_SCALED_UINT (nr_serving_cells[i]->sinr,             0xFFFFFFFF, -23,    "\t\t            SINR", " dB");
+            PRINT_VALIDATED_UINT64      (nr_serving_cells[i]->timing_advance,   0xFFFFFFFFFFFFFFFF, "\t\t  Timing advance", " us");
+        }
+    } else
+        g_print ("Serving NR cells: n/a\n");
+
+    if (nr_neighboring_cells_count && nr_neighboring_cells) {
+        guint i;
+
+        g_print ("Neighboring NR cells: %d\n", nr_neighboring_cells_count);
+        for (i = 0; i < nr_neighboring_cells_count; i++) {
+            g_autofree gchar *system_subtype_str = NULL;
+
+            system_subtype_str = mbim_data_subclass_build_string_from_mask (nr_neighboring_cells[i]->system_sub_type);
+            g_print ("\tNeighboring cell [%u]:\n"
+                     "\t\t  System subtype: %s\n"
+                     "\t\t     Provider id: %s\n"
+                     "\t\t         Cell id: %s\n",
+                     i + 1,
+                     VALIDATE_UNKNOWN (system_subtype_str),
+                     VALIDATE_UNKNOWN (nr_neighboring_cells[i]->provider_id),
+                     VALIDATE_UNKNOWN (nr_neighboring_cells[i]->cell_id));
+            PRINT_VALIDATED_UINT        (nr_neighboring_cells[i]->physical_cell_id, 0xFFFFFFFF,       "\t\tPhysical cell id", NULL);
+            PRINT_VALIDATED_UINT        (nr_neighboring_cells[i]->tac,              0xFFFFFFFF,       "\t\t             TAC", NULL);
+            PRINT_VALIDATED_SCALED_UINT (nr_neighboring_cells[i]->rsrp,             0xFFFFFFFF, -156, "\t\t            RSRP", " dBm");
+            PRINT_VALIDATED_SCALED_UINT (nr_neighboring_cells[i]->rsrq,             0xFFFFFFFF, -43,  "\t\t            RSRQ", " dB");
+            PRINT_VALIDATED_SCALED_UINT (nr_neighboring_cells[i]->sinr,             0xFFFFFFFF, -23,  "\t\t            SINR", " dB");
+        }
+    } else
+        g_print ("Neighboring NR cells: n/a\n");
+
+#undef PRINT_VALIDATED_SCALED_UINT
+#undef PRINT_VALIDATED_UINT64
+#undef PRINT_VALIDATED_UINT
+#undef PRINT_VALIDATED_INT
+
+    shutdown (TRUE);
+}
+
+static void
+query_version_ready (MbimDevice   *device,
+                     GAsyncResult *res)
+{
+    g_autoptr(MbimMessage) response = NULL;
+    g_autoptr(GError)      error = NULL;
+    guint16                mbim_version;
+    guint16                mbim_ext_version;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        shutdown (FALSE);
+        return;
+    }
+
+    g_print ("[%s] Successfully exchanged version information\n",
+             mbim_device_get_path_display (device));
+    if (!mbim_message_ms_basic_connect_extensions_v2_version_response_parse (
+            response,
+            &mbim_version,
+            &mbim_ext_version,
+            &error)) {
+        g_printerr ("error: couldn't parse response message: %s\n", error->message);
+        shutdown (FALSE);
+        return;
+    }
+
+    g_print (" MBIM version          : %x.%02x\n", mbim_version >> 8, mbim_version & 0xFF);
+    g_print (" MBIM extended version : %x.%02x\n", mbim_ext_version >> 8, mbim_ext_version & 0xFF);
+
+    shutdown (TRUE);
+    return;
+}
+
+typedef struct {
+    MbimMicoMode                 mico_mode;
+    gboolean                     mico_mode_set;
+    MbimDrxCycle                 drx_cycle;
+    gboolean                     drx_cycle_set;
+    MbimLadnInfo                 ladn_info;
+    gboolean                     ladn_info_set;
+    MbimDefaultPduActivationHint pdu_hint;
+    gboolean                     pdu_hint_set;
+    gboolean                     re_register_if_needed;
+    gboolean                     re_register_if_needed_set;
+} RegistrationParameters;
+
+static gboolean
+set_registration_parameters_foreach_cb (const gchar             *key,
+                                        const gchar             *value,
+                                        GError                 **error,
+                                        RegistrationParameters  *params)
+{
+    if (g_ascii_strcasecmp (key, "mico-mode") == 0) {
+        if (!mbimcli_read_mico_mode_from_string (value, &params->mico_mode)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown mico-mode: '%s'", value);
+            return FALSE;
+        }
+        params->mico_mode_set = TRUE;
+    } else if (g_ascii_strcasecmp (key, "drx-cycle") == 0) {
+        if (!mbimcli_read_drx_cycle_from_string (value, &params->drx_cycle)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown drx-cycle: '%s'", value);
+            return FALSE;
+        }
+        params->drx_cycle_set = TRUE;
+    } else if (g_ascii_strcasecmp (key, "ladn-info") == 0) {
+        if (!mbimcli_read_ladn_info_from_string (value, &params->ladn_info)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown ladn-info: '%s'", value);
+            return FALSE;
+        }
+        params->ladn_info_set = TRUE;
+    } else if (g_ascii_strcasecmp (key, "default-pdu-activation-hint") == 0) {
+        if (!mbimcli_read_default_pdu_activation_hint_from_string (value, &params->pdu_hint)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown default-pdu-activation-hint: '%s'", value);
+            return FALSE;
+        }
+        params->pdu_hint_set = TRUE;
+    } else if (g_ascii_strcasecmp (key, "re-register-if-needed") == 0) {
+        if (!mbimcli_read_boolean_from_string (value, &params->re_register_if_needed)) {
+            g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS,
+                         "unknown re-register-if-needed: '%s'", value);
+            return FALSE;
+        }
+        params->re_register_if_needed_set = TRUE;
+    } else {
+        g_set_error (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED,
+                     "unrecognized option '%s'", key);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+registration_parameters_ready (MbimDevice   *device,
+                               GAsyncResult *res)
+{
+    g_autoptr(MbimMessage)       response = NULL;
+    g_autoptr(GError)            error = NULL;
+    MbimMicoMode                 mico_mode;
+    MbimDrxCycle                 drx_cycle;
+    MbimLadnInfo                 ladn_info;
+    MbimDefaultPduActivationHint pdu_hint;
+    gboolean                     re_register_if_nedeed;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        shutdown (FALSE);
+        return;
+    }
+
+    g_print ("[%s] Successfully received registration parameters information\n",
+             mbim_device_get_path_display (device));
+    if (!mbim_message_ms_basic_connect_extensions_v3_registration_parameters_response_parse (
+            response,
+            &mico_mode,
+            &drx_cycle,
+            &ladn_info,
+            &pdu_hint,
+            &re_register_if_nedeed,
+            NULL, /* ignore unnamed IEs for now */
+            &error)) {
+        g_printerr ("error: couldn't parse response message: %s\n", error->message);
+        shutdown (FALSE);
+        return;
+    }
+
+    g_print ("\t             MICO mode: %s\n", mbim_mico_mode_get_string (mico_mode));
+    g_print ("\t             DRX cycle: %s\n", mbim_drx_cycle_get_string (drx_cycle));
+    g_print ("\t      LADN information: %s\n", mbim_ladn_info_get_string (ladn_info));
+    g_print ("\tDefault PDU activation: %s\n", mbim_default_pdu_activation_hint_get_string (pdu_hint));
+    g_print ("\t Re-register if needed: %s\n", re_register_if_nedeed ? "yes" : "no");
+
+    shutdown (TRUE);
+}
+
+static void
+query_modem_configuration_ready (MbimDevice   *device,
+                                 GAsyncResult *res)
+{
+    g_autoptr(MbimMessage)         response = NULL;
+    g_autoptr(GError)              error = NULL;
+    MbimModemConfigurationStatus   configuration_status;
+    g_autofree gchar              *configuration_name = NULL;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        shutdown (FALSE);
+        return;
+    }
+
+    if (!mbim_message_ms_basic_connect_extensions_v3_modem_configuration_response_parse (
+            response,
+            &configuration_status,
+            &configuration_name,
+            NULL, /* ignore unnamed IEs for now */
+            &error)) {
+        g_printerr ("error: couldn't parse response message: %s\n", error->message);
+        shutdown (FALSE);
+        return;
+    }
+
+    g_print ("[%s] Modem configuration retrieved: \n",
+             mbim_device_get_path_display (device));
+    g_print ("\tStatus: '%s'\n"
+             "\t  Name: '%s'\n",
+             VALIDATE_UNKNOWN (mbim_modem_configuration_status_get_string (configuration_status)),
+             VALIDATE_UNKNOWN (configuration_name));
+
+    shutdown (TRUE);
+}
+
+static void
+query_wake_reason_ready (MbimDevice   *device,
+                         GAsyncResult *res)
+{
+    g_autoptr(MbimMessage)  response = NULL;
+    g_autoptr(GError)       error = NULL;
+    MbimWakeType            wake_type;
+    guint32                 session_id;
+    MbimTlv                *wake_tlv = NULL;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        shutdown (FALSE);
+        return;
+    }
+
+    if (!mbim_message_ms_basic_connect_extensions_v3_wake_reason_response_parse (
+            response,
+            &wake_type,
+            &session_id,
+            &wake_tlv,
+            &error)) {
+        g_printerr ("error: couldn't parse response message: %s\n", error->message);
+        shutdown (FALSE);
+        return;
+    }
+
+    g_print ("[%s] Successfully queried wake reason\n",
+             mbim_device_get_path_display (device));
+
+    g_print ("\t Wake type: '%s'\n", mbim_wake_type_get_string (wake_type));
+    g_print ("\tSession ID: '%u'\n", session_id);
+
+    if ((wake_type == MBIM_WAKE_TYPE_CID_RESPONSE) ||
+        (wake_type == MBIM_WAKE_TYPE_CID_INDICATION)) {
+        const MbimUuid    *service = NULL;
+        g_autofree gchar  *service_str = NULL;
+        guint32            cid = 0;
+        guint32            payload_size = 0;
+        g_autofree guint8 *payload = NULL;
+        g_autofree gchar  *payload_str = NULL;
+
+        if (!mbim_tlv_wake_command_get (wake_tlv,
+                                        &service,
+                                        &cid,
+                                        &payload_size,
+                                        &payload,
+                                        &error)) {
+            g_printerr ("error: couldn't parse wake command TLV: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+
+        /* Known payload defined right now only for the Connect CID */
+        if ((mbim_uuid_to_service (service) == MBIM_SERVICE_BASIC_CONNECT) &&
+            (cid == MBIM_CID_BASIC_CONNECT_CONNECT) &&
+            (payload_size == 4)) {
+            guint32 activate;
+
+            memcpy (&activate, payload, payload_size);
+            activate = GUINT32_FROM_LE (activate);
+            if (activate == 0x00000001 || activate == 0x00000000)
+                payload_str = g_strdup (activate ? "activate" : "deactivate");
+        }
+
+        if (!payload_str)
+            payload_str = mbim_common_str_hex (payload, payload_size, ':');
+
+        service_str = mbim_uuid_get_printable (service);
+
+        g_print ("\t   Service: '%s'\n", service_str);
+        g_print ("\t       CID: '0x%08x'\n", cid);
+        g_print ("\t   Payload: '%s'\n", payload_str);
+        shutdown (TRUE);
+        return;
+    }
+
+    if (wake_type == MBIM_WAKE_TYPE_PACKET) {
+        guint32            filter_id = 0;
+        guint32            original_packet_size = 0;
+        guint32            packet_size = 0;
+        g_autofree guint8 *packet = NULL;
+        g_autofree gchar  *packet_str = NULL;
+
+        if (!mbim_tlv_wake_packet_get (wake_tlv,
+                                       &filter_id,
+                                       &original_packet_size,
+                                       &packet_size,
+                                       &packet,
+                                       &error)) {
+            g_printerr ("error: couldn't parse wake packet TLV: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+
+        packet_str = mbim_common_str_hex (packet, packet_size, ':');
+
+        g_print ("\t    Filter ID: '%u'\n", filter_id);
+        g_print ("\tOriginal size: '%u'\n", original_packet_size);
+        g_print ("\t   Saved size: '%u'\n", packet_size);
+        g_print ("\t       Packet: '%s'\n", packet_str);
+
+        shutdown (TRUE);
+        return;
+    }
+
+    g_printerr ("error: unknown wake type: 0x%08x\n", wake_type);
+    shutdown (FALSE);
+}
+
 void
 mbimcli_ms_basic_connect_extensions_run (MbimDevice   *device,
                                          GCancellable *cancellable)
@@ -672,7 +1688,6 @@
         return;
     }
 
-    /* Request to query LTE attach configuration? */
     if (query_lte_attach_configuration_flag) {
         g_debug ("Asynchronously querying LTE attach configuration...");
         request = mbim_message_ms_basic_connect_extensions_lte_attach_configuration_query_new (NULL);
@@ -685,7 +1700,6 @@
         return;
     }
 
-    /* Request to query LTE attach info? */
     if (query_lte_attach_status_flag || query_lte_attach_info_flag) {
         g_debug ("Asynchronously querying LTE attach info...");
         request = mbim_message_ms_basic_connect_extensions_lte_attach_info_query_new (NULL);
@@ -721,8 +1735,7 @@
                              NULL);
         return;
     }
-    
-    /*Request to query slot information status? */
+
     if (query_slot_info_status_str) {
         guint32 slot_index;
 
@@ -743,7 +1756,6 @@
         return;
     }
 
-    /*Request to set device slot mappings */
     if (set_device_slot_mappings_str) {
         g_autoptr(GPtrArray) slot_array = NULL;
 
@@ -766,7 +1778,6 @@
         return;
     }
 
-    /*Request to query device slot mappings? */
     if (query_device_slot_mappings_flag) {
         g_debug ("Asynchronously querying device slot mappings...");
         request = mbim_message_ms_basic_connect_extensions_device_slot_mappings_query_new (NULL);
@@ -779,5 +1790,245 @@
         return;
     }
 
+    if (query_location_info_status_flag) {
+        g_debug ("Asynchronously querying location info status...");
+        request = mbim_message_ms_basic_connect_extensions_location_info_status_query_new (NULL);
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)query_location_info_status_ready,
+                             NULL);
+        return;
+    }
+
+    if (set_provisioned_contexts_str) {
+        g_auto(ProvisionedContextProperties) props = {
+            .access_string   = NULL,
+            .operation       = MBIM_CONTEXT_OPERATION_DELETE,
+            .auth_protocol   = MBIM_AUTH_PROTOCOL_NONE,
+            .username        = NULL,
+            .password        = NULL,
+            .ip_type         = MBIM_CONTEXT_IP_TYPE_DEFAULT,
+            .state           = MBIM_CONTEXT_STATE_DISABLED,
+            .roaming_control = MBIM_CONTEXT_ROAMING_CONTROL_HOME_ONLY,
+            .media_type      = MBIM_CONTEXT_MEDIA_TYPE_CELLULAR_ONLY,
+            .source          = MBIM_CONTEXT_SOURCE_ADMIN,
+            .compression     = MBIM_COMPRESSION_NONE,
+            .context_type    = MBIM_CONTEXT_TYPE_INVALID
+        };
+
+        if (!mbimcli_parse_key_value_string (set_provisioned_contexts_str,
+                                             &error,
+                                             (MbimParseKeyValueForeachFn)set_provisioned_contexts_foreach_cb,
+                                             &props)) {
+            g_printerr ("error: couldn't parse input string: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+
+        request = mbim_message_ms_basic_connect_extensions_provisioned_contexts_set_new (
+                      props.operation,
+                      mbim_uuid_from_context_type (props.context_type),
+                      props.ip_type,
+                      props.state,
+                      props.roaming_control,
+                      props.media_type,
+                      props.source,
+                      props.access_string,
+                      props.username,
+                      props.password,
+                      props.compression,
+                      props.auth_protocol,
+                      &error);
+
+        if (!request) {
+            g_printerr ("error: couldn't create request: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+
+        mbim_device_command (ctx->device,
+                             request,
+                             60,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)provisioned_contexts_ready,
+                             NULL);
+        return;
+    }
+
+    /* Request to query Provisioned contexts? */
+    if (query_provisioned_contexts_flag) {
+        g_debug ("Asynchronously query provisioned contexts...");
+
+        request = mbim_message_ms_basic_connect_extensions_provisioned_contexts_query_new (NULL);
+        if (!request) {
+            g_printerr ("error: couldn't create request: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)provisioned_contexts_ready,
+                             NULL);
+        return;
+    }
+
+    if (query_base_stations_flag) {
+        g_debug ("Asynchronously querying base stations...");
+        /* default capacity is 15, so use that value when querying */
+        request = mbim_message_ms_basic_connect_extensions_base_stations_info_query_new (15, 15, 15, 15, 15, NULL);
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)query_base_stations_ready,
+                             NULL);
+        return;
+    }
+
+    if (query_version_str) {
+        guint16       bcd_mbim_version = 0;
+        guint16       bcd_mbim_extended_version = 0;
+        guint8        mbim_version_major = 0;
+        guint8        mbim_version_minor = 0;
+        guint8        mbim_extended_version_major = 0;
+        guint8        mbim_extended_version_minor = 0;
+        g_auto(GStrv) split = NULL;
+        g_auto(GStrv) mbim_version = NULL;
+        g_auto(GStrv) mbim_extended_version = NULL;
+
+        split = g_strsplit (query_version_str, ",", -1);
+
+        if (g_strv_length (split) > 2) {
+            g_printerr ("error: couldn't parse input string, too many arguments\n");
+            return;
+        }
+
+        if (g_strv_length (split) < 2) {
+            g_printerr ("error: couldn't parse input string, missing arguments\n");
+            return;
+        }
+
+        mbim_version = g_strsplit (split[0], ".", -1);
+        if (!mbimcli_read_uint8_from_bcd_string (mbim_version[0], &mbim_version_major) ||
+            !mbimcli_read_uint8_from_bcd_string (mbim_version[1], &mbim_version_minor)) {
+            g_printerr ("error: couldn't parse version string\n");
+            return;
+        }
+        bcd_mbim_version = mbim_version_major << 8 | mbim_version_minor;
+        g_debug ("BCD version built: 0x%x", bcd_mbim_version);
+
+        mbim_extended_version = g_strsplit (split[1], ".", -1);
+        if (!mbimcli_read_uint8_from_bcd_string (mbim_extended_version[0], &mbim_extended_version_major) ||
+            !mbimcli_read_uint8_from_bcd_string (mbim_extended_version[1], &mbim_extended_version_minor)) {
+            g_printerr ("error: couldn't parse extended version string\n");
+            return;
+        }
+        bcd_mbim_extended_version = mbim_extended_version_major << 8 | mbim_extended_version_minor;
+        g_debug ("BCD extended version built: 0x%x", bcd_mbim_extended_version);
+
+        g_debug ("Asynchronously querying Version...");
+        request = mbim_message_ms_basic_connect_extensions_v2_version_query_new (bcd_mbim_version, bcd_mbim_extended_version, NULL);
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)query_version_ready,
+                             NULL);
+        return;
+    }
+
+    if (query_registration_parameters_flag) {
+        g_debug (" Asynchronously querying registration parameters...");
+        request = mbim_message_ms_basic_connect_extensions_v3_registration_parameters_query_new (NULL);
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)registration_parameters_ready,
+                             NULL);
+        return;
+    }
+
+    if (set_registration_parameters_str) {
+        RegistrationParameters params = { 0 };
+
+        if (!mbimcli_parse_key_value_string (set_registration_parameters_str,
+                                             &error,
+                                             (MbimParseKeyValueForeachFn)set_registration_parameters_foreach_cb,
+                                             &params)) {
+            g_printerr ("error: couldn't parse input string: %s\n", error->message);
+            shutdown (FALSE);
+            return;
+        }
+
+        if (!params.mico_mode_set ||
+            !params.drx_cycle_set ||
+            !params.ladn_info_set ||
+            !params.pdu_hint_set  ||
+            !params.re_register_if_needed_set) {
+            g_printerr ("error: missing required keys\n");
+            if (!params.mico_mode_set)
+                g_printerr ("error: key 'mico-mode' is missing\n");
+            if (!params.drx_cycle_set)
+                g_printerr ("error: key 'drx-cycle' is missing\n");
+            if (!params.ladn_info_set)
+                g_printerr ("error: key 'ladn-info' is missing\n");
+            if (!params.pdu_hint_set)
+                g_printerr ("error: key 'default-pdu-activation-hint' is missing\n");
+            if (!params.re_register_if_needed_set)
+                g_printerr ("error: key 're-register-is-needed' is missing\n");
+            shutdown (FALSE);
+            return;
+        }
+
+        g_debug ("Asynchronously set registration parameters\n");
+        request = (mbim_message_ms_basic_connect_extensions_v3_registration_parameters_set_new (
+                       params.mico_mode,
+                       params.drx_cycle,
+                       params.ladn_info,
+                       params.pdu_hint,
+                       params.re_register_if_needed,
+                       NULL, /* ignore unnamed IEs for now */
+                       NULL));
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)registration_parameters_ready,
+                             NULL);
+        return;
+    }
+
+    /* Request to query modem configuration? */
+    if (query_modem_configuration_flag) {
+        g_debug ("Asynchronously query modem configuration\n");
+        request = mbim_message_ms_basic_connect_extensions_v3_modem_configuration_query_new (NULL);
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)query_modem_configuration_ready,
+                             NULL);
+        return;
+    }
+
+    /* Request to query Wake Reason? */
+    if (query_wake_reason_flag) {
+        g_debug ("Asynchronously querying wake reason...");
+        request = mbim_message_ms_basic_connect_extensions_v3_wake_reason_query_new (NULL);
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)query_wake_reason_ready,
+                             NULL);
+        return;
+    }
+
     g_warn_if_reached ();
 }
diff --git a/src/mbimcli/mbimcli-ms-sar.c b/src/mbimcli/mbimcli-ms-sar.c
index 7f612e4..247454f 100644
--- a/src/mbimcli/mbimcli-ms-sar.c
+++ b/src/mbimcli/mbimcli-ms-sar.c
@@ -157,18 +157,21 @@
              "\t                Mode: %s\n"
              "\t       Backoff state: %s\n"
              "\tWi-Fi hardware state: %s\n"
-             "\t       Config States: (%u)\n",
+             "\t       Config states: %u\n",
              mbim_device_get_path_display (device),
              VALIDATE_UNKNOWN (mode_str),
              VALIDATE_UNKNOWN (backoff_state_str),
              VALIDATE_UNKNOWN (wifi_integration_str),
              config_states_count);
+
+
     for (i = 0; i < config_states_count; i++) {
-        g_print ("\t\t[%u] Antenna index: %u\n"
-                 "\t\t     Backoff index: %u\n",
-                 i,
-                 config_states[i]->antenna_index,
-                 config_states[i]->backoff_index);
+        g_print ("\t\t[%u]\n", i);
+        if (config_states[i]->antenna_index == 0xFFFFFFFF)
+            g_print ("\t\t  Antenna index: all\n");
+        else
+            g_print ("\t\t  Antenna index: %u\n", config_states[i]->antenna_index);
+        g_print ("\t\t  Backoff index: %u\n", config_states[i]->backoff_index);
     }
 
     shutdown (TRUE);
@@ -242,49 +245,37 @@
         return FALSE;
     }
 
-    if (g_ascii_strcasecmp (split[0], "device") == 0) {
-        *mode = MBIM_SAR_CONTROL_MODE_DEVICE;
-    } else if (g_ascii_strcasecmp (split[0], "os") == 0) {
-        *mode = MBIM_SAR_CONTROL_MODE_OS;
-    } else {
-        g_printerr ("error: invalid mode: '%s', it must be device or os\n", split[0]);
+    if (!mbimcli_read_sar_control_mode_from_string (split[0], mode)) {
+        g_printerr ("error: invalid mode: '%s'\n", split[0]);
         return FALSE;
     }
 
-    if (g_ascii_strcasecmp (split[1], "disabled") == 0) {
-        *state = MBIM_SAR_BACKOFF_STATE_DISABLED;
-    } else if (g_ascii_strcasecmp (split[1], "enabled") == 0) {
-        *state = MBIM_SAR_BACKOFF_STATE_ENABLED;
-    } else {
-        g_printerr ("error: invalid state: '%s', it must be enabled or disabled\n", split[1]);
+    if (!mbimcli_read_sar_backoff_state_from_string (split[1], state)) {
+        g_printerr ("error: invalid state: '%s'\n", split[1]);
         return FALSE;
     }
 
     /* Check whether we have the optional item array: [{antenna_index,backoff_index}...] */
     if (split[2]) {
-        const gchar *state_begin;
+        const gchar      *array_begin;
+        const gchar      *array_end;
+        g_autofree gchar *array_str = NULL;
 
-        state_begin = strchr (split[2], '[');
-        if (state_begin != NULL) {
-            *states_array = g_ptr_array_new_with_free_func (g_free);
-            while ((state_begin = strchr (state_begin, '{')) != NULL) {
-                guint32 antenna_index;
-                guint32 backoff_index;
+        array_begin = strchr (split[2], '[');
+        array_end   = strchr (split[2], ']');
 
-                if (sscanf (state_begin, "{%d,%d}", &antenna_index, &backoff_index) == 2) {
-                    MbimSarConfigState *config_state;
-
-                    config_state = g_new (MbimSarConfigState, 1);
-                    config_state->antenna_index = antenna_index;
-                    config_state->backoff_index = backoff_index;
-                    g_ptr_array_add (*states_array, config_state);
-                    ++state_begin;
-                } else {
-                    break;
-                }
-            }
+        if (!array_begin || !array_end || (array_begin > array_end)) {
+            g_printerr ("error: invalid SAR config state array: '%s'\n", split[2]);
+            return FALSE;
         }
-    }
+
+        array_str = g_strndup (&array_begin[1], array_end - array_begin - 1);
+        if (!mbimcli_parse_sar_config_state_array (array_str, states_array)) {
+            g_printerr ("error: failure parsing the SAR config state array contents: '%s'\n", array_str);
+            return FALSE;
+        }
+    } else
+        *states_array = NULL;
 
     return TRUE;
 }
@@ -345,24 +336,20 @@
     /* Request to set SAR config */
     if (set_sar_config_str) {
         g_autoptr(GPtrArray) states_array = NULL;
+        MbimSarControlMode   mode;
+        MbimSarBackoffState  state;
 
-        MbimSarControlMode         mode;
-        MbimSarBackoffState        state;
-        guint                      states_count = 0;
-        const MbimSarConfigState **states_ptrs  = NULL;
-
-        g_print ("Asynchronously set sar config\n");
+        g_debug ("Asynchronously setting SAR config");
         if (!sar_config_input_parse (set_sar_config_str, &mode, &state, &states_array)) {
             shutdown (FALSE);
             return;
         }
 
-        if (states_array != NULL) {
-            states_count = states_array->len;
-            states_ptrs  = (const MbimSarConfigState **)states_array->pdata;
-        }
-
-        request = mbim_message_ms_sar_config_set_new (mode, state, states_count, states_ptrs, NULL);
+        request = mbim_message_ms_sar_config_set_new (mode,
+                                                      state,
+                                                      states_array ? states_array->len : 0,
+                                                      states_array ? (const MbimSarConfigState **)states_array->pdata : NULL,
+                                                      NULL);
         mbim_device_command (ctx->device,
                              request,
                              10,
diff --git a/src/mbimcli/mbimcli.c b/src/mbimcli/mbimcli.c
index 1161b7c..d68142d 100644
--- a/src/mbimcli/mbimcli.c
+++ b/src/mbimcli/mbimcli.c
@@ -37,6 +37,8 @@
 /* Main options */
 static gchar *device_str;
 static gboolean device_open_proxy_flag;
+static gboolean device_open_ms_mbimex_v2_flag;
+static gboolean device_open_ms_mbimex_v3_flag;
 static gchar *no_open_str;
 static gboolean no_close_flag;
 static gboolean noop_flag;
@@ -53,6 +55,14 @@
       "Request to use the 'mbim-proxy' proxy",
       NULL
     },
+    { "device-open-ms-mbimex-v2", 0, 0, G_OPTION_ARG_NONE, &device_open_ms_mbimex_v2_flag,
+      "Request to enable Microsoft MBIMEx v2.0 support",
+      NULL
+    },
+    { "device-open-ms-mbimex-v3", 0, 0, G_OPTION_ARG_NONE, &device_open_ms_mbimex_v3_flag,
+      "Request to enable Microsoft MBIMEx v3.0 support",
+      NULL
+    },
     { "no-open", 0, 0, G_OPTION_ARG_STRING, &no_open_str,
       "Do not explicitly open the MBIM device before running the command",
       "[Transaction ID]"
@@ -332,6 +342,10 @@
     /* Setup device open flags */
     if (device_open_proxy_flag)
         open_flags |= MBIM_DEVICE_OPEN_FLAGS_PROXY;
+    if (device_open_ms_mbimex_v2_flag)
+        open_flags |= MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V2;
+    if (device_open_ms_mbimex_v3_flag)
+        open_flags |= MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V3;
 
     /* Open the device */
     mbim_device_open_full (device,
@@ -413,6 +427,11 @@
         exit (EXIT_FAILURE);
     }
 
+    if (device_open_ms_mbimex_v2_flag && device_open_ms_mbimex_v3_flag) {
+        g_printerr ("error: cannot request both MBIMEx v2.0 and 3.0 at the same time\n");
+        exit (EXIT_FAILURE);
+    }
+
     /* Go on! */
 }
 
diff --git a/utils/meson.build b/utils/meson.build
index 24412a6..280afab 100644
--- a/utils/meson.build
+++ b/utils/meson.build
@@ -6,4 +6,5 @@
   output: '@BASENAME@',
   configuration: version_conf,
   install_dir: mbim_bindir,
+  install_mode: 'rwxr-xr-x',
 )