Merge remote-tracking branch 'cros/upstream' into 'cros/master'
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0fa0bd3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,76 @@
+*~
+*.o
+*.lo
+*.la
+*.bz2
+
+ChangeLog
+INSTALL
+Makefile
+Makefile.in
+README
+aclocal.m4
+autom4te.cache
+compile
+config.cache
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+install-sh
+libtool
+ltmain.sh
+m4
+missing
+stamp-h1
+depcomp
+gtk-doc.make
+test-driver
+
+build-aux/mbim-codegen/*.pyc
+
+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.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
+
+libmbim-glib/.deps
+libmbim-glib/.libs
+libmbim-glib/mbim-version.h
+
+libmbim-glib/test/.deps
+libmbim-glib/test/.libs
+libmbim-glib/test/test-uuid
+libmbim-glib/test/test-cid
+libmbim-glib/test/test-message
+libmbim-glib/test/test-fragment
+libmbim-glib/test/test-message-parser
+libmbim-glib/test/test-message-builder
+libmbim-glib/test/*.log
+libmbim-glib/test/*.trs
+
+libmbim-glib/generated/.deps
+libmbim-glib/generated/.libs
+libmbim-glib/generated/*.c
+libmbim-glib/generated/*.h
+libmbim-glib/generated/*.sections
+
+cli/.deps
+cli/.libs
+cli/mbimcli
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..8f0552f
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Aleksander Morgado <aleksander@gnu.org>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..bf50f20
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,482 @@
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+    		    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the 
+    Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
+    Boston, MA  02111-1307  USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..b9cce85
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,8 @@
+
+SUBDIRS = . build-aux data libmbim-glib cli utils docs
+
+ACLOCAL_AMFLAGS = -I m4
+
+DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc
+
+EXTRA_DIST = gtester.make
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..046c152
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,88 @@
+
+Overview of changes in libmbim 1.4
+----------------------------------------
+
+ * Added support for additional MBIM messages:
+    MBIM_SMS_CONFIGURATION
+    MBIM_SMS_READ
+    MBIM_SMS_SEND
+    MBIM_SMS_DELETE
+    MBIM_SMS_MESSAGE_STORE_STATUS
+    MBIM_USSD
+    MBIM_PHONEBOOK_CONFIGURATION
+    MBIM_PHONEBOOK_READ
+    MBIM_PHONEBOOK_DELETE
+    MBIM_PHONEBOOK_WRITE
+    MBIM_STK_PAC
+    MBIM_STK_TERMINAL_RESPONSE
+    MBIM_STK_ENVELOPE
+    MBIM_DEVICE_SERVICE_SUBSCRIBER_LIST
+    MBIM_AUTH_AKA
+    MBIM_AUTH_AKAP
+    MBIM_AUTH_SIM
+    MBIM_PACKET_STATISTICS
+    MBIM_NETWORK_IDLE_HINT
+    MBIM_EMERGENCY_MODE
+    MBIM_IP_PACKET_FILTERS
+    MBIM_DSS_CONNECT
+    MBIM_MULTICARRIER_PROVIDERS
+
+ * Updated mbimcli with new commands:
+    --query-packet-statistics
+
+ * Use gtester to run unit tests.
+
+Overview of changes in libmbim 1.2
+----------------------------------------
+
+ * Added support for additional MBIM messages:
+    MBIM_VISIBLE_PROVIDERS
+    MBIM_PREFERRED_PROVIDERS
+    MBIM_HOME_PROVIDER
+    MBIM_SERVICE_ACTIVATION
+
+ * Updated mbimcli with new commands:
+    --query-visible-providers
+    --query-preferred-providers
+    --query-home-provider
+    --query-signal-state
+    --no-open
+    --noop
+
+ * Updated mbim-network with session support, keeping TRID sequence between
+   commands.
+
+ * New symbols to check library version.
+
+
+Overview of changes in libmbim 1.0
+----------------------------------------
+
+ * Updated mbimcli with new commands:
+    --enter-pin
+    --change-pin
+    --enable-pin
+    --disable-pin
+    --enter-puk
+    --query-registration-state
+    --register-automatic
+    --query-packet-service-state
+    --attach-packet-service
+    --detach-packet-service
+    --query-connection-state
+    --connect
+    --disconnect
+    --no-close
+
+ * Removed the 'basic-connect' prefix from mbimcli commands.
+
+ * New 'mbim-network' script to help launch a connection through the
+   shell.
+
+ * Added gtk-doc documentation
+
+
+Overview of changes in libmbim 0.0.1
+----------------------------------------
+
+Initial release.
\ No newline at end of file
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..a1fecad
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+REQUIRED_AUTOMAKE_VERSION=1.9
+PKG_NAME=libmbim
+
+(test -f $srcdir/configure.ac \
+  && test -f $srcdir/libmbim-glib/libmbim-glib.h) || {
+    echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
+    echo " top-level $PKG_NAME directory"
+    exit 1
+}
+
+(cd $srcdir;
+    gtkdocize || exit 1
+    mkdir -p m4
+    touch README NEWS ChangeLog
+    autoreconf --force --install --verbose
+    if test -z "$NOCONFIGURE"; then
+        ./configure --enable-maintainer-mode "$@"
+    fi
+)
diff --git a/build-aux/Makefile.am b/build-aux/Makefile.am
new file mode 100644
index 0000000..987cbaa
--- /dev/null
+++ b/build-aux/Makefile.am
@@ -0,0 +1,2 @@
+
+SUBDIRS = templates mbim-codegen
diff --git a/build-aux/mbim-codegen/Makefile.am b/build-aux/mbim-codegen/Makefile.am
new file mode 100644
index 0000000..9dc56aa
--- /dev/null
+++ b/build-aux/mbim-codegen/Makefile.am
@@ -0,0 +1,9 @@
+
+EXTRA_DIST = \
+	utils.py \
+	Struct.py \
+	Message.py \
+	ObjectList.py \
+	mbim-codegen
+
+CLEANFILES = *.pyc
diff --git a/build-aux/mbim-codegen/Message.py b/build-aux/mbim-codegen/Message.py
new file mode 100644
index 0000000..cbeb239
--- /dev/null
+++ b/build-aux/mbim-codegen/Message.py
@@ -0,0 +1,875 @@
+#!/usr/bin/env python
+# -*- 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
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+#
+
+import string
+import utils
+
+
+"""
+Flag the values which always need to be read
+"""
+def flag_always_read_field(fields, field_name):
+    for field in fields:
+        if field['name'] == field_name:
+            if field['format'] != 'guint32':
+                    raise ValueError('Fields to always read \'%s\' must be a guint32' % field_name)
+            field['always-read'] = True
+            return
+    raise ValueError('Couldn\'t find field to always read \'%s\'' % field_name)
+
+
+"""
+Validate fields in the dictionary
+"""
+def validate_fields(fields):
+    for field in fields:
+        # Look for condition fields, which need to be always read
+        if 'available-if' in field:
+            condition = field['available-if']
+            flag_always_read_field(fields, condition['field'])
+
+        # Look for array size fields, which need to be always read
+        if field['format'] == 'byte-array':
+            pass
+        elif field['format'] == 'ref-byte-array':
+            pass
+        elif field['format'] == 'ref-byte-array-no-offset':
+            pass
+        elif field['format'] == 'unsized-byte-array':
+            pass
+        elif field['format'] == 'uuid':
+            pass
+        elif field['format'] == 'guint32':
+            pass
+        elif field['format'] == 'guint32-array':
+            flag_always_read_field(fields, field['array-size-field'])
+        elif field['format'] == 'guint64':
+            pass
+        elif field['format'] == 'guint64-array':
+            flag_always_read_field(fields, field['array-size-field'])
+        elif field['format'] == 'string':
+            pass
+        elif field['format'] == 'string-array':
+            flag_always_read_field(fields, field['array-size-field'])
+        elif field['format'] == 'struct':
+            if 'struct-type' not in field:
+                raise ValueError('Field type \'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')
+        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')
+        elif field['format'] == 'ipv4':
+            pass
+        elif field['format'] == 'ref-ipv4':
+            pass
+        elif field['format'] == 'ipv4-array':
+            flag_always_read_field(fields, field['array-size-field'])
+        elif field['format'] == 'ipv6':
+            pass
+        elif field['format'] == 'ref-ipv6':
+            pass
+        elif field['format'] == 'ipv6-array':
+            flag_always_read_field(fields, field['array-size-field'])
+        else:
+            raise ValueError('Cannot handle field type \'%s\'' % field['format'])
+
+
+"""
+The Message class takes care of all message handling
+"""
+class Message:
+
+    """
+    Constructor
+    """
+    def __init__(self, dictionary):
+        # The message service, e.g. "Basic Connect"
+        self.service = dictionary['service']
+
+        # The name of the specific message, e.g. "Something"
+        self.name = dictionary['name']
+
+        # Query
+        if 'query' in dictionary:
+            self.has_query = True
+            self.query = dictionary['query']
+            validate_fields(self.query)
+        else:
+            self.has_query = False
+            self.query = []
+
+        # Set
+        if 'set' in dictionary:
+            self.has_set = True
+            self.set = dictionary['set']
+            validate_fields(self.set)
+        else:
+            self.has_set = False
+            self.set = []
+
+
+        # Response
+        if 'response' in dictionary:
+            self.has_response = True
+            self.response = dictionary['response']
+            validate_fields(self.response)
+        else:
+            self.has_response = False
+            self.response = []
+
+        # Notification
+        if 'notification' in dictionary:
+            self.has_notification = True
+            self.notification = dictionary['notification']
+            validate_fields(self.notification)
+        else:
+            self.has_notification = False
+            self.notification = []
+
+        # Build Fullname
+        if self.service == 'Basic Connect':
+            self.fullname = 'MBIM Message ' + self.name
+        elif self.name == "":
+            self.fullname = 'MBIM Message ' + self.service
+        else:
+            self.fullname = 'MBIM Message ' + self.service + ' ' + self.name
+
+        # Build CID enum
+        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()
+
+
+    """
+    Emit the message handling implementation
+    """
+    def emit(self, hfile, cfile):
+        if self.has_query:
+            utils.add_separator(hfile, 'Message (Query)', self.fullname);
+            utils.add_separator(cfile, 'Message (Query)', self.fullname);
+            self._emit_message_creator(hfile, cfile, 'query', self.query)
+
+        if self.has_set:
+            utils.add_separator(hfile, 'Message (Set)', self.fullname);
+            utils.add_separator(cfile, 'Message (Set)', self.fullname);
+            self._emit_message_creator(hfile, cfile, 'set', self.set)
+
+        if self.has_response:
+            utils.add_separator(hfile, 'Message (Response)', self.fullname);
+            utils.add_separator(cfile, 'Message (Response)', self.fullname);
+            self._emit_message_parser(hfile, cfile, 'response', self.response)
+
+        if self.has_notification:
+            utils.add_separator(hfile, 'Message (Notification)', self.fullname);
+            utils.add_separator(cfile, 'Message (Notification)', self.fullname);
+            self._emit_message_parser(hfile, cfile, 'notification', self.notification)
+
+
+    """
+    Emit message creator
+    """
+    def _emit_message_creator(self, hfile, cfile, message_type, fields):
+        translations = { 'message'                  : 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(),
+                         'cid_enum_name'            : self.cid_enum_name }
+        template = (
+            '\n'
+            'MbimMessage *${underscore}_${message_type}_new (\n')
+
+        for field in fields:
+            translations['field'] = utils.build_underscore_name_from_camelcase (field['name'])
+            translations['struct'] = field['struct-type'] if 'struct-type' in field else ''
+            translations['public'] = field['public-format'] if 'public-format' in field else field['format']
+
+            if field['format'] == 'byte-array':
+                inner_template = ('    const guint8 *${field},\n')
+            elif field['format'] == 'unsized-byte-array' or \
+                 field['format'] == 'ref-byte-array' or \
+                 field['format'] == 'ref-byte-array-no-offset':
+                inner_template = ('    const guint32 ${field}_size,\n'
+                                  '    const guint8 *${field},\n')
+            elif field['format'] == 'uuid':
+                inner_template = ('    const MbimUuid *${field},\n')
+            elif field['format'] == 'guint32':
+                inner_template = ('    ${public} ${field},\n')
+            elif field['format'] == 'guint32-array':
+                inner_template = ('    const ${public} *${field},\n')
+            elif field['format'] == 'guint64':
+                inner_template = ('    ${public} ${field},\n')
+            elif field['format'] == 'guint64-array':
+                inner_template = ('    const ${public} *${field},\n')
+            elif field['format'] == 'string':
+                inner_template = ('    const gchar *${field},\n')
+            elif field['format'] == 'string-array':
+                inner_template = ('    const gchar *const *${field},\n')
+            elif field['format'] == 'struct':
+                inner_template = ('    const ${struct} *${field},\n')
+            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'] == 'ipv4':
+                inner_template = ('    const MbimIPv4 *${field},\n')
+            elif field['format'] == 'ref-ipv4':
+                inner_template = ('    const MbimIPv4 *${field},\n')
+            elif field['format'] == 'ipv4-array':
+                inner_template = ('    const MbimIPv4 *${field},\n')
+            elif field['format'] == 'ipv6':
+                inner_template = ('    const MbimIPv6 *${field},\n')
+            elif field['format'] == 'ref-ipv6':
+                inner_template = ('    const MbimIPv6 *${field},\n')
+            elif field['format'] == 'ipv6-array':
+                inner_template = ('    const MbimIPv6 *${field},\n')
+
+            template += (string.Template(inner_template).substitute(translations))
+
+        template += (
+            '    GError **error);\n')
+        hfile.write(string.Template(template).substitute(translations))
+
+        template = (
+            '\n'
+            '/**\n'
+            ' * ${underscore}_${message_type}_new:\n')
+
+        for field in fields:
+            translations['name'] = field['name']
+            translations['field'] = utils.build_underscore_name_from_camelcase (field['name'])
+            translations['struct'] = field['struct-type'] if 'struct-type' in field else ''
+            translations['public'] = field['public-format'] if 'public-format' in field else field['format']
+            translations['array_size'] = field['array-size'] if 'array-size' in field else ''
+
+            if field['format'] == 'byte-array':
+                inner_template = (' * @${field}: the \'${name}\' field, given as an array of ${array_size} #guint8 values.\n')
+            elif field['format'] == 'unsized-byte-array' or \
+                 field['format'] == 'ref-byte-array' or \
+                 field['format'] == 'ref-byte-array-no-offset':
+                inner_template = (' * @${field}_size: size of the ${field} array.\n'
+                                  ' * @${field}: the \'${name}\' field, given as an array of #guint8 values.\n')
+            elif field['format'] == 'uuid':
+                inner_template = (' * @${field}: the \'${name}\' field, given as a #MbimUuid.\n')
+            elif field['format'] == 'guint32':
+                inner_template = (' * @${field}: the \'${name}\' field, given as a #${public}.\n')
+            elif field['format'] == 'guint32-array':
+                inner_template = (' * @${field}: the \'${name}\' field, given as an array of #${public}.\n')
+            elif field['format'] == 'guint64':
+                inner_template = (' * @${field}: the \'${name}\' field, given as a #${public}.\n')
+            elif field['format'] == 'guint64-array':
+                inner_template = (' * @${field}: the \'${name}\' field, given as an array of #${public}.\n')
+            elif field['format'] == 'string':
+                inner_template = (' * @${field}: the \'${name}\' field, given as a string.\n')
+            elif field['format'] == 'string-array':
+                inner_template = (' * @${field}: the \'${name}\' field, given as an array of strings.\n')
+            elif field['format'] == 'struct':
+                inner_template = (' * @${field}: the \'${name}\' field, given as a #${struct}.\n')
+            elif field['format'] == 'struct-array':
+                inner_template = (' * @${field}: the \'${name}\' field, given as an array of #${struct}s.\n')
+            elif field['format'] == 'ref-struct-array':
+                inner_template = (' * @${field}: the \'${name}\' field, given as an array of #${struct}s.\n')
+            elif field['format'] == 'ipv4':
+                inner_template = (' * @${field}: the \'${name}\' field, given as a #MbimIPv4.\n')
+            elif field['format'] == 'ref-ipv4':
+                inner_template = (' * @${field}: the \'${name}\' field, given as a #MbimIPv4.\n')
+            elif field['format'] == 'ipv4-array':
+                inner_template = (' * @${field}: the \'${name}\' field, given as an array of #MbimIPv4.\n')
+            elif field['format'] == 'ipv6':
+                inner_template = (' * @${field}: the \'${name}\' field, given as a #MbimIPv6.\n')
+            elif field['format'] == 'ref-ipv6':
+                inner_template = (' * @${field}: the \'${name}\' field, given as a #MbimIPv6.\n')
+            elif field['format'] == 'ipv6-array':
+                inner_template = (' * @${field}: the \'${name}\' field, given as an array of #MbimIPv6.\n')
+
+            template += (string.Template(inner_template).substitute(translations))
+
+        template += (
+            ' * @error: return location for error or %NULL.\n'
+            ' *\n'
+            ' * Create a new request for the \'${message}\' ${message_type} command in the \'${service}\' service.\n'
+            ' *\n'
+            ' * Returns: a newly allocated #MbimMessage, which should be freed with mbim_message_unref().\n'
+            ' */\n'
+            'MbimMessage *\n'
+            '${underscore}_${message_type}_new (\n')
+
+        for field in fields:
+            translations['field'] = utils.build_underscore_name_from_camelcase (field['name'])
+            translations['struct'] = field['struct-type'] if 'struct-type' in field else ''
+            translations['public'] = field['public-format'] if 'public-format' in field else field['format']
+            translations['array_size'] = field['array-size'] if 'array-size' in field else ''
+
+            if field['format'] == 'byte-array':
+                inner_template = ('    const guint8 *${field},\n')
+            elif field['format'] == 'unsized-byte-array' or \
+                 field['format'] == 'ref-byte-array' or \
+                 field['format'] == 'ref-byte-array-no-offset':
+                inner_template = ('    const guint32 ${field}_size,\n'
+                                  '    const guint8 *${field},\n')
+            elif field['format'] == 'uuid':
+                inner_template = ('    const MbimUuid *${field},\n')
+            elif field['format'] == 'guint32':
+                inner_template = ('    ${public} ${field},\n')
+            elif field['format'] == 'guint32-array':
+                inner_template = ('    const ${public} *${field},\n')
+            elif field['format'] == 'guint64':
+                inner_template = ('    ${public} ${field},\n')
+            elif field['format'] == 'guint64-array':
+                inner_template = ('    const ${public} *${field},\n')
+            elif field['format'] == 'string':
+                inner_template = ('    const gchar *${field},\n')
+            elif field['format'] == 'string-array':
+                inner_template = ('    const gchar *const *${field},\n')
+            elif field['format'] == 'struct':
+                inner_template = ('    const ${struct} *${field},\n')
+            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'] == 'ipv4':
+                inner_template = ('    const MbimIPv4 *${field},\n')
+            elif field['format'] == 'ref-ipv4':
+                inner_template = ('    const MbimIPv4 *${field},\n')
+            elif field['format'] == 'ipv4-array':
+                inner_template = ('    const MbimIPv4 *${field},\n')
+            elif field['format'] == 'ipv6':
+                inner_template = ('    const MbimIPv6 *${field},\n')
+            elif field['format'] == 'ref-ipv6':
+                inner_template = ('    const MbimIPv6 *${field},\n')
+            elif field['format'] == 'ipv6-array':
+                inner_template = ('    const MbimIPv6 *${field},\n')
+
+            template += (string.Template(inner_template).substitute(translations))
+
+        template += (
+            '    GError **error)\n'
+            '{\n'
+            '    MbimMessageCommandBuilder *builder;\n'
+            '\n'
+            '    builder = _mbim_message_command_builder_new (0,\n'
+            '                                                 MBIM_SERVICE_${service_underscore_upper},\n'
+            '                                                 ${cid_enum_name},\n'
+            '                                                 MBIM_MESSAGE_COMMAND_TYPE_${message_type_upper});\n')
+
+        for field in fields:
+            translations['field'] = utils.build_underscore_name_from_camelcase(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'] = field['struct-type'] if 'struct-type' in field else ''
+            translations['struct_underscore'] = utils.build_underscore_name_from_camelcase (translations['struct'])
+            translations['array_size'] = field['array-size'] if 'array-size' in field else ''
+
+            inner_template = ''
+            if 'available-if' in field:
+                condition = field['available-if']
+                translations['condition_field'] = utils.build_underscore_name_from_camelcase(condition['field'])
+                translations['condition_operation'] = condition['operation']
+                translations['condition_value'] = condition['value']
+                inner_template += (
+                    '    if (${condition_field} ${condition_operation} ${condition_value}) {\n')
+            else:
+                inner_template += ('    {\n')
+
+            if field['format'] == 'byte-array':
+                inner_template += ('        _mbim_message_command_builder_append_byte_array (builder, FALSE, FALSE, ${field}, ${array_size});\n')
+            elif field['format'] == 'unsized-byte-array':
+                inner_template += ('        _mbim_message_command_builder_append_byte_array (builder, FALSE, FALSE, ${field}, ${field}_size);\n')
+            elif field['format'] == 'ref-byte-array':
+                inner_template += ('        _mbim_message_command_builder_append_byte_array (builder, TRUE, TRUE, ${field}, ${field}_size);\n')
+            elif field['format'] == 'ref-byte-array-no-offset':
+                inner_template += ('        _mbim_message_command_builder_append_byte_array (builder, FALSE, TRUE, ${field}, ${field}_size);\n')
+            elif field['format'] == 'uuid':
+                inner_template += ('        _mbim_message_command_builder_append_uuid (builder, ${field});\n')
+            elif field['format'] == 'guint32':
+                inner_template += ('        _mbim_message_command_builder_append_guint32 (builder, ${field});\n')
+            elif field['format'] == 'guint32-array':
+                inner_template += ('        _mbim_message_command_builder_append_guint32_array (builder, ${field}, ${array_size_field});\n')
+            elif field['format'] == 'guint64':
+                inner_template += ('        _mbim_message_command_builder_append_guint64 (builder, ${field});\n')
+            elif field['format'] == 'string':
+                inner_template += ('        _mbim_message_command_builder_append_string (builder, ${field});\n')
+            elif field['format'] == 'string-array':
+                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'] == 'struct-array':
+                inner_template += ('        _mbim_message_command_builder_append_${struct_underscore}_struct_array (builder, ${field}, ${array_size_field}, FALSE);\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')
+            elif field['format'] == 'ipv4':
+                inner_template += ('        _mbim_message_command_builder_append_ipv4 (builder, ${field}, FALSE);\n')
+            elif field['format'] == 'ref-ipv4':
+                inner_template += ('        _mbim_message_command_builder_append_ipv4 (builder, ${field}, TRUE);\n')
+            elif field['format'] == 'ipv4-array':
+                inner_template += ('        _mbim_message_command_builder_append_ipv4_array (builder, ${field}, ${array_size_field});\n')
+            elif field['format'] == 'ipv6':
+                inner_template += ('        _mbim_message_command_builder_append_ipv6 (builder, ${field}, FALSE);\n')
+            elif field['format'] == 'ref-ipv6':
+                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')
+            else:
+                raise ValueError('Cannot handle field type \'%s\'' % field['format'])
+
+            inner_template += ('    }\n')
+
+            template += (string.Template(inner_template).substitute(translations))
+
+        template += (
+            '\n'
+            '    return _mbim_message_command_builder_complete (builder);\n'
+            '}\n')
+        cfile.write(string.Template(template).substitute(translations))
+
+
+    """
+    Emit message parser
+    """
+    def _emit_message_parser(self, hfile, cfile, message_type, fields):
+        translations = { '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() }
+        template = (
+            '\n'
+            'gboolean ${underscore}_${message_type}_parse (\n'
+            '    const MbimMessage *message,\n')
+
+        for field in fields:
+            translations['field'] = utils.build_underscore_name_from_camelcase(field['name'])
+            translations['public'] = field['public-format'] if 'public-format' in field else field['format']
+            translations['struct'] = field['struct-type'] if 'struct-type' in field else ''
+
+            if field['format'] == 'byte-array':
+                inner_template = ('    const guint8 **${field},\n')
+            elif field['format'] == 'unsized-byte-array' or field['format'] == 'ref-byte-array':
+                inner_template = ('    guint32 *${field}_size,\n'
+                                  '    const guint8 **${field},\n')
+            elif field['format'] == 'uuid':
+                inner_template = ('    const MbimUuid **${field},\n')
+            elif field['format'] == 'guint32':
+                inner_template = ('    ${public} *${field},\n')
+            elif field['format'] == 'guint32-array':
+                inner_template = ('    ${public} **${field},\n')
+            elif field['format'] == 'guint64':
+                inner_template = ('    ${public} *${field},\n')
+            elif field['format'] == 'guint64-array':
+                inner_template = ('    ${public} **${field},\n')
+            elif field['format'] == 'string':
+                inner_template = ('    gchar **${field},\n')
+            elif field['format'] == 'string-array':
+                inner_template = ('    gchar ***${field},\n')
+            elif field['format'] == 'struct':
+                inner_template = ('    ${struct} **${field},\n')
+            elif field['format'] == 'struct-array':
+                inner_template = ('    ${struct} ***${field},\n')
+            elif field['format'] == 'ref-struct-array':
+                inner_template = ('    ${struct} ***${field},\n')
+            elif field['format'] == 'ipv4':
+                inner_template = ('    const MbimIPv4 **${field},\n')
+            elif field['format'] == 'ref-ipv4':
+                inner_template = ('    const MbimIPv4 **${field},\n')
+            elif field['format'] == 'ipv4-array':
+                inner_template = ('    MbimIPv4 **${field},\n')
+            elif field['format'] == 'ipv6':
+                inner_template = ('    const MbimIPv6 **${field},\n')
+            elif field['format'] == 'ref-ipv6':
+                inner_template = ('    const MbimIPv6 **${field},\n')
+            elif field['format'] == 'ipv6-array':
+                inner_template = ('    MbimIPv6 **${field},\n')
+            else:
+                raise ValueError('Cannot handle field type \'%s\'' % field['format'])
+
+            template += (string.Template(inner_template).substitute(translations))
+
+        template += (
+            '    GError **error);\n')
+        hfile.write(string.Template(template).substitute(translations))
+
+        template = (
+            '\n'
+            '/**\n'
+            ' * ${underscore}_${message_type}_parse:\n'
+            ' * @message: the #MbimMessage.\n')
+
+        for field in fields:
+            translations['field'] = utils.build_underscore_name_from_camelcase(field['name'])
+            translations['name'] = field['name']
+            translations['public'] = field['public-format'] if 'public-format' in field else field['format']
+            translations['struct'] = field['struct-type'] if 'struct-type' in field else ''
+            translations['struct_underscore'] = utils.build_underscore_name_from_camelcase (translations['struct'])
+            translations['array_size'] = field['array-size'] if 'array-size' in field else ''
+
+            if field['format'] == 'byte-array':
+                inner_template = (' * @${field}: return location for an array of ${array_size} #guint8 values. Do not free the returned value, it is owned by @message.\n')
+            elif field['format'] == 'unsized-byte-array' or field['format'] == 'ref-byte-array':
+                inner_template = (' * @${field}_size: return location for the size of the ${field} array.\n'
+                                  ' * @${field}: 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 = (' * @${field}: 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'] == 'guint32':
+                inner_template = (' * @${field}: return location for a #${public}, or %NULL if the \'${name}\' field is not needed.\n')
+            elif field['format'] == 'guint32-array':
+                inner_template = (' * @${field}: return location for a newly allocated array of #${public}s, or %NULL if the \'${name}\' field is not needed. Free the returned value with g_free().\n')
+            elif field['format'] == 'guint64':
+                inner_template = (' * @${field}: return location for a #guint64, or %NULL if the \'${name}\' field is not needed.\n')
+            elif field['format'] == 'guint64-array':
+                inner_template = (' * @${field}: return location for a newly allocated array of #guint64s, or %NULL if the \'${name}\' field is not needed. Free the returned value with g_free().\n')
+            elif field['format'] == 'string':
+                inner_template = (' * @${field}: 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'] == 'string-array':
+                inner_template = (' * @${field}: 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 = (' * @${field}: 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'] == 'struct-array':
+                inner_template = (' * @${field}: return location for a newly allocated array of #${struct}s, 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 = (' * @${field}: return location for a newly allocated array of #${struct}s, or %NULL if the \'${name}\' field is not needed. Free the returned value with ${struct_underscore}_array_free().\n')
+            elif field['format'] == 'ipv4':
+                inner_template = (' * @${field}: 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':
+                inner_template = (' * @${field}: 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'] == 'ipv4-array':
+                inner_template = (' * @${field}: return location for a newly allocated array of #MbimIPv4s, or %NULL if the \'${name}\' field is not needed. Free the returned value with g_free().\n')
+            elif field['format'] == 'ipv6':
+                inner_template = (' * @${field}: 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'] == 'ref-ipv6':
+                inner_template = (' * @${field}: 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 = (' * @${field}: return location for a newly allocated array of #MbimIPv6s, or %NULL if the \'${name}\' field is not needed. Free the returned value with g_free().\n')
+
+            template += (string.Template(inner_template).substitute(translations))
+
+        template += (
+            ' * @error: return location for error or %NULL.\n'
+            ' *\n'
+            ' * Create a new request for the \'${name}\' ${message_type} command in the \'${service}\' service.\n'
+            ' *\n'
+            ' * Returns: %TRUE if the message was correctly parsed, %FALSE if @error is set.\n'
+            ' */\n'
+            'gboolean\n'
+            '${underscore}_${message_type}_parse (\n'
+            '    const MbimMessage *message,\n')
+
+        for field in fields:
+            translations['field'] = utils.build_underscore_name_from_camelcase(field['name'])
+            translations['public'] = field['public-format'] if 'public-format' in field else field['format']
+            translations['struct'] = field['struct-type'] if 'struct-type' in field else ''
+
+            if field['format'] == 'byte-array':
+                inner_template = ('    const guint8 **${field},\n')
+            elif field['format'] == 'unsized-byte-array' or field['format'] == 'ref-byte-array':
+                inner_template = ('    guint32 *${field}_size,\n'
+                                  '    const guint8 **${field},\n')
+            elif field['format'] == 'uuid':
+                inner_template = ('    const MbimUuid **${field},\n')
+            elif field['format'] == 'guint32':
+                inner_template = ('    ${public} *${field},\n')
+            elif field['format'] == 'guint32-array':
+                inner_template = ('    ${public} **${field},\n')
+            elif field['format'] == 'guint64':
+                inner_template = ('    ${public} *${field},\n')
+            elif field['format'] == 'guint64-array':
+                inner_template = ('    ${public} **${field},\n')
+            elif field['format'] == 'string':
+                inner_template = ('    gchar **${field},\n')
+            elif field['format'] == 'string-array':
+                inner_template = ('    gchar ***${field},\n')
+            elif field['format'] == 'struct':
+                inner_template = ('    ${struct} **${field},\n')
+            elif field['format'] == 'struct-array':
+                inner_template = ('    ${struct} ***${field},\n')
+            elif field['format'] == 'ref-struct-array':
+                inner_template = ('    ${struct} ***${field},\n')
+            elif field['format'] == 'ipv4':
+                inner_template = ('    const MbimIPv4 **${field},\n')
+            elif field['format'] == 'ref-ipv4':
+                inner_template = ('    const MbimIPv4 **${field},\n')
+            elif field['format'] == 'ipv4-array':
+                inner_template = ('    MbimIPv4 **${field},\n')
+            elif field['format'] == 'ipv6':
+                inner_template = ('    const MbimIPv6 **${field},\n')
+            elif field['format'] == 'ref-ipv6':
+                inner_template = ('    const MbimIPv6 **${field},\n')
+            elif field['format'] == 'ipv6-array':
+                inner_template = ('    MbimIPv6 **${field},\n')
+
+            template += (string.Template(inner_template).substitute(translations))
+
+        template += (
+            '    GError **error)\n'
+            '{\n')
+
+        if fields != []:
+            template += (
+                '    guint32 offset = 0;\n')
+
+        for field in fields:
+            if 'always-read' in field:
+                translations['field'] = utils.build_underscore_name_from_camelcase(field['name'])
+                inner_template = ('    guint32 _${field};\n')
+                template += (string.Template(inner_template).substitute(translations))
+
+        if message_type == 'response':
+            template += (
+                '\n'
+                '    if (mbim_message_get_message_type (message) != MBIM_MESSAGE_TYPE_COMMAND_DONE) {\n'
+                '        g_set_error (error,\n'
+                '                     MBIM_CORE_ERROR,\n'
+                '                     MBIM_CORE_ERROR_INVALID_MESSAGE,\n'
+                '                     \"Message is not a response\");\n'
+                '        return FALSE;\n'
+                '    }\n')
+        elif message_type == 'notification':
+            template += (
+                '\n'
+                '    if (mbim_message_get_message_type (message) != MBIM_MESSAGE_TYPE_INDICATE_STATUS) {\n'
+                '        g_set_error (error,\n'
+                '                     MBIM_CORE_ERROR,\n'
+                '                     MBIM_CORE_ERROR_INVALID_MESSAGE,\n'
+                '                     \"Message is not a notification\");\n'
+                '        return FALSE;\n'
+                '    }\n')
+        else:
+            raise ValueError('Unexpected message type \'%s\'' % message_type)
+
+        for field in fields:
+            translations['field']                   = utils.build_underscore_name_from_camelcase(field['name'])
+            translations['field_format_underscore'] = utils.build_underscore_name_from_camelcase(field['format'])
+            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 ''
+            translations['struct_type'] = field['struct-type'] if 'struct-type' in field else ''
+            translations['array_size'] = field['array-size'] if 'array-size' in field else ''
+
+            inner_template = (
+                '\n'
+                '    /* Read the \'${field_name}\' variable */\n')
+            if 'available-if' in field:
+                condition = field['available-if']
+                translations['condition_field'] = utils.build_underscore_name_from_camelcase(condition['field'])
+                translations['condition_operation'] = condition['operation']
+                translations['condition_value'] = condition['value']
+                inner_template += (
+                    '    if (!(_${condition_field} ${condition_operation} ${condition_value})) {\n')
+                if field['format'] == 'byte-array':
+                    inner_template += (
+                        '        if (${field})\n'
+                        '            *${field} = NULL;\n')
+                elif field['format'] == 'unsized-byte-array' or \
+                   field['format'] == 'ref-byte-array':
+                    inner_template += (
+                        '        if (${field}_size)\n'
+                        '            *${field}_size = 0;\n'
+                        '        if (${field})\n'
+                        '            *${field} = NULL;\n')
+                elif field['format'] == 'guint32-array' or \
+                     field['format'] == 'string' or \
+                     field['format'] == 'string-array' or \
+                     field['format'] == 'struct' or \
+                     field['format'] == 'struct-array' or \
+                     field['format'] == 'ref-struct-array' or \
+                     field['format'] == 'ipv4' or \
+                     field['format'] == 'ref-ipv4' or \
+                     field['format'] == 'ipv4-array' or \
+                     field['format'] == 'ipv6' or \
+                     field['format'] == 'ref-ipv6' or \
+                     field['format'] == 'ipv6-array':
+                    inner_template += (
+                        '        if (${field} != NULL)\n'
+                        '            *${field} = NULL;\n')
+                else:
+                    raise ValueError('Field format \'%s\' unsupported as optional field' % field['format'])
+
+                inner_template += (
+                    '    } else {\n')
+            else:
+                inner_template += (
+                    '    {\n')
+
+            if 'always-read' in field:
+                inner_template += (
+                    '        _${field} = _mbim_message_read_guint32 (message, offset);\n'
+                    '        if (${field} != NULL)\n'
+                    '            *${field} = _${field};\n'
+                    '        offset += 4;\n')
+            elif field['format'] == 'byte-array':
+                inner_template += (
+                    '        const guint8 *tmp;\n'
+                    '\n'
+                    '        tmp = _mbim_message_read_byte_array (message, 0, offset, FALSE, FALSE, NULL);\n'
+                    '        if (${field} != NULL)\n'
+                    '            *${field} = tmp;\n'
+                    '        offset += ${array_size};\n')
+            elif field['format'] == 'unsized-byte-array':
+                inner_template += (
+                    '        const guint8 *tmp;\n'
+                    '        guint32 tmpsize;\n'
+                    '\n'
+                    '        tmp = _mbim_message_read_byte_array (message, 0, offset, FALSE, FALSE, &tmpsize);\n'
+                    '        if (${field} != NULL)\n'
+                    '            *${field} = tmp;\n'
+                    '        if (${field}_size != NULL)\n'
+                    '            *${field}_size = tmpsize;\n'
+                    '        offset += tmpsize;\n')
+            elif field['format'] == 'ref-byte-array':
+                inner_template += (
+                    '        const guint8 *tmp;\n'
+                    '        guint32 tmpsize;\n'
+                    '\n'
+                    '        tmp = _mbim_message_read_byte_array (message, 0, offset, TRUE, TRUE, &tmpsize);\n'
+                    '        if (${field} != NULL)\n'
+                    '            *${field} = tmp;\n'
+                    '        if (${field}_size != NULL)\n'
+                    '            *${field}_size = tmpsize;\n'
+                    '        offset += 8;\n')
+            elif field['format'] == 'uuid':
+                inner_template += (
+                    '        if (${field} != NULL)\n'
+                    '            *${field} =  _mbim_message_read_uuid (message, offset);\n'
+                    '        offset += 16;\n')
+            elif field['format'] == 'guint32':
+                inner_template += (
+                    '        if (${field} != NULL)\n'
+                    '            *${field} =  _mbim_message_read_guint32 (message, offset);\n'
+                    '        offset += 4;\n')
+            elif field['format'] == 'guint32-array':
+                inner_template += (
+                    '        if (${field} != NULL)\n'
+                    '            *${field} = _mbim_message_read_guint32_array (message, _{array_size_field}, offset);\n'
+                    '        offset += (4 * _${array_size_field});\n')
+            elif field['format'] == 'guint64':
+                inner_template += (
+                    '        if (${field} != NULL)\n'
+                    '            *${field} =  _mbim_message_read_guint64 (message, offset);\n'
+                    '        offset += 8;\n')
+            elif field['format'] == 'string':
+                inner_template += (
+                    '        if (${field} != NULL)\n'
+                    '            *${field} = _mbim_message_read_string (message, 0, offset);\n'
+                    '        offset += 8;\n')
+            elif field['format'] == 'string-array':
+                inner_template += (
+                    '        if (${field} != NULL)\n'
+                    '            *${field} = _mbim_message_read_string_array (message, _${array_size_field}, 0, offset);\n'
+                    '        offset += (8 * _${array_size_field});\n')
+            elif field['format'] == 'struct':
+                inner_template += (
+                    '        ${struct_type} *tmp;\n'
+                    '        guint32 bytes_read = 0;\n'
+                    '\n'
+                    '        tmp = _mbim_message_read_${struct_name}_struct (message, offset, &bytes_read);\n'
+                    '        if (${field} != NULL)\n'
+                    '            *${field} = tmp;\n'
+                    '        else\n'
+                    '             _${struct_name}_free (tmp);\n'
+                    '        offset += bytes_read;\n')
+            elif field['format'] == 'struct-array':
+                inner_template += (
+                    '        if (${field} != NULL)\n'
+                    '            *${field} = _mbim_message_read_${struct_name}_struct_array (message, _${array_size_field}, offset, FALSE);\n'
+                    '        offset += 4;\n')
+            elif field['format'] == 'ref-struct-array':
+                inner_template += (
+                    '        if (${field} != NULL)\n'
+                    '            *${field} = _mbim_message_read_${struct_name}_struct_array (message, _${array_size_field}, offset, TRUE);\n'
+                    '        offset += (8 * _${array_size_field});\n')
+            elif field['format'] == 'ipv4':
+                inner_template += (
+                    '        if (${field} != NULL)\n'
+                    '            *${field} =  _mbim_message_read_ipv4 (message, offset, FALSE);\n'
+                    '        offset += 4;\n')
+            elif field['format'] == 'ref-ipv4':
+                inner_template += (
+                    '        if (${field} != NULL)\n'
+                    '            *${field} =  _mbim_message_read_ipv4 (message, offset, TRUE);\n'
+                    '        offset += 4;\n')
+            elif field['format'] == 'ipv4-array':
+                inner_template += (
+                    '        if (${field} != NULL)\n'
+                    '            *${field} =  _mbim_message_read_ipv4_array (message, _${array_size_field}, offset);\n'
+                    '        offset += 4;\n')
+            elif field['format'] == 'ipv6':
+                inner_template += (
+                    '        if (${field} != NULL)\n'
+                    '            *${field} =  _mbim_message_read_ipv6 (message, offset, FALSE);\n'
+                    '        offset += 4;\n')
+            elif field['format'] == 'ref-ipv6':
+                inner_template += (
+                    '        if (${field} != NULL)\n'
+                    '            *${field} =  _mbim_message_read_ipv6 (message, offset, TRUE);\n'
+                    '        offset += 4;\n')
+            elif field['format'] == 'ipv6-array':
+                inner_template += (
+                    '        if (${field} != NULL)\n'
+                    '            *${field} =  _mbim_message_read_ipv6_array (message, _${array_size_field}, offset);\n'
+                    '        offset += 4;\n')
+
+            inner_template += (
+                '    }\n')
+
+            template += (string.Template(inner_template).substitute(translations))
+
+        template += (
+            '\n'
+            '    return TRUE;\n'
+            '}\n')
+        cfile.write(string.Template(template).substitute(translations))
+
+
+    """
+    Emit the section content
+    """
+    def emit_section_content(self, sfile):
+        translations = { 'name_dashed' : utils.build_dashed_name(self.name),
+                         'underscore'  : utils.build_underscore_name(self.fullname) }
+
+        template = (
+            '\n'
+            '<SUBSECTION ${name_dashed}>\n')
+        sfile.write(string.Template(template).substitute(translations))
+
+        if self.has_query:
+            template = (
+                '${underscore}_query_new\n')
+            sfile.write(string.Template(template).substitute(translations))
+
+        if self.has_set:
+            template = (
+                '${underscore}_set_new\n')
+            sfile.write(string.Template(template).substitute(translations))
+
+        if self.has_response:
+            template = (
+                '${underscore}_response_parse\n')
+            sfile.write(string.Template(template).substitute(translations))
+
+        if self.has_notification:
+            template = (
+                '${underscore}_notification_parse\n')
+            sfile.write(string.Template(template).substitute(translations))
diff --git a/build-aux/mbim-codegen/ObjectList.py b/build-aux/mbim-codegen/ObjectList.py
new file mode 100644
index 0000000..ddb5a68
--- /dev/null
+++ b/build-aux/mbim-codegen/ObjectList.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+# -*- 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
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+#
+
+import string
+
+from Message import Message
+from Struct import Struct
+import utils
+
+"""
+Check field to see if it holds a struct
+"""
+def set_struct_usage(struct, fields):
+    for field in fields:
+        if field['format'] == 'struct' and field['struct-type'] == struct.name:
+            struct.single_member = True
+            break
+        if field['format'] == 'ref-struct-array' and field['struct-type'] == struct.name:
+            struct.array_member = True
+            break
+        if field['format'] == 'struct-array' and field['struct-type'] == struct.name:
+            struct.array_member = True
+            break
+
+
+"""
+The ObjectList class handles the generation of all commands and types for a given
+specific service
+"""
+class ObjectList:
+
+    """
+    Constructor
+    """
+    def __init__(self, objects_dictionary):
+        self.command_list = []
+        self.struct_list = []
+        self.service = ''
+
+        # Loop items in the list, creating Message objects for the messages
+        for object_dictionary in objects_dictionary:
+            if object_dictionary['type'] == 'Command':
+                self.command_list.append(Message(object_dictionary))
+            elif object_dictionary['type'] == 'Struct':
+                self.struct_list.append(Struct(object_dictionary))
+            elif object_dictionary['type'] == 'Service':
+                self.service = object_dictionary['name']
+            else:
+                raise ValueError('Cannot handle object type \'%s\'' % object_dictionary['type'])
+
+        if self.service == '':
+            raise ValueError('Service name not specified')
+
+        # Populate struct usages
+        for struct in self.struct_list:
+            for command in self.command_list:
+                set_struct_usage(struct, command.query)
+                set_struct_usage(struct, command.set)
+                set_struct_usage(struct, command.response)
+                set_struct_usage(struct, command.notification)
+
+    """
+    Emit the structs and commands handling implementation
+    """
+    def emit(self, hfile, cfile):
+        # Emit all structs
+        for item in self.struct_list:
+            item.emit(hfile, cfile)
+        # Emit all commands
+        for item in self.command_list:
+            item.emit(hfile, cfile)
+
+
+    """
+    Emit the sections
+    """
+    def emit_sections(self, sfile):
+        translations = { 'service_dashed' : utils.build_dashed_name(self.service),
+                         'service'        : self.service }
+
+        # Emit section header
+        template = (
+            '\n'
+            '<SECTION>\n'
+            '<FILE>mbim-${service_dashed}</FILE>\n'
+            '<TITLE>${service}</TITLE>\n')
+        sfile.write(string.Template(template).substitute(translations))
+
+        # Emit subsection per type
+        for struct in self.struct_list:
+            struct.emit_section_content(sfile)
+
+        # Emit subsection per command
+        for command in self.command_list:
+            command.emit_section_content(sfile)
+
+        sfile.write(
+            '</SECTION>\n')
diff --git a/build-aux/mbim-codegen/Struct.py b/build-aux/mbim-codegen/Struct.py
new file mode 100644
index 0000000..afe2ba5
--- /dev/null
+++ b/build-aux/mbim-codegen/Struct.py
@@ -0,0 +1,688 @@
+#!/usr/bin/env python
+# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil -*-
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+#
+
+import string
+
+import utils
+
+"""
+The Struct class takes care of emitting the struct type
+"""
+class Struct:
+
+    """
+    Constructor
+    """
+    def __init__(self, dictionary):
+        self.name = dictionary['name']
+        self.contents = dictionary['contents']
+
+        # 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
+
+        # Check whether the struct is composed of fixed-sized fields
+        self.size = 0
+        for field in self.contents:
+            if field['format'] == 'guint32':
+                self.size += 4
+            elif field['format'] == 'guint64':
+                self.size += 8
+            elif field['format'] == 'uuid':
+                self.size += 16
+            elif field['format'] == 'ipv4':
+                self.size += 4
+            elif field['format'] == 'ipv6':
+                self.size += 16
+            else:
+                self.size = 0
+                break
+
+
+    """
+    Emit the new struct type
+    """
+    def _emit_type(self, hfile):
+        translations = { 'name' : self.name }
+        template = (
+            '\n'
+            '/**\n'
+            ' * ${name}:\n')
+        for field in self.contents:
+            translations['field_name_underscore'] = utils.build_underscore_name_from_camelcase(field['name'])
+            if field['format'] == 'uuid':
+                inner_template = (
+                    ' * @${field_name_underscore}: a #MbimUuid.\n')
+            elif field['format'] =='byte-array':
+                inner_template = (' * @${field_name_underscore}: an array of #guint8 values.\n')
+            elif field['format'] =='unsized-byte-array' or field['format'] == 'ref-byte-array':
+                inner_template = ''
+                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'] == 'guint32':
+                inner_template = (
+                    ' * @${field_name_underscore}: a #guint32.\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')
+            elif field['format'] == 'string':
+                inner_template = (
+                    ' * @${field_name_underscore}: a string.\n')
+            elif field['format'] == 'string-array':
+                inner_template = (
+                    ' * @${field_name_underscore}: an array of strings.\n')
+            elif field['format'] == 'ipv4':
+                inner_template = (
+                    ' * @${field_name_underscore}: a #MbimIPv4.\n')
+            elif field['format'] == 'ref-ipv4':
+                inner_template = (
+                    ' * @${field_name_underscore}: a #MbimIPv4.\n')
+            elif field['format'] == 'ipv4-array':
+                inner_template = (
+                    ' * @${field_name_underscore}: an array of #MbimIPv4 values.\n')
+            elif field['format'] == 'ipv6':
+                inner_template = (
+                    ' * @${field_name_underscore}: a #MbimIPv6\n')
+            elif field['format'] == 'ref-ipv6':
+                inner_template = (
+                    ' * @${field_name_underscore}: a #MbimIPv6\n')
+            elif field['format'] == 'ipv6-array':
+                inner_template = (
+                    ' * @${field_name_underscore}: an array of #MbimIPv6 values.\n')
+            else:
+                raise ValueError('Cannot handle format \'%s\' in struct' % field['format'])
+            template += string.Template(inner_template).substitute(translations)
+
+        template += (
+            ' */\n'
+            'typedef struct {\n')
+        for field in self.contents:
+            translations['field_name_underscore'] = utils.build_underscore_name_from_camelcase(field['name'])
+            if field['format'] == 'uuid':
+                inner_template = (
+                    '    MbimUuid ${field_name_underscore};\n')
+            elif field['format'] == 'byte-array':
+                translations['array_size'] = field['array-size']
+                inner_template = (
+                    '    guint8 ${field_name_underscore}[${array_size}];\n')
+            elif field['format'] == 'unsized-byte-array' or field['format'] == 'ref-byte-array':
+                inner_template = ''
+                if 'array-size-field' not in field:
+                    inner_template += (
+                        '    guint32 ${field_name_underscore}_size;\n')
+                inner_template += (
+                    '    guint8 *${field_name_underscore};\n')
+            elif field['format'] == 'guint32':
+                inner_template = (
+                    '    guint32 ${field_name_underscore};\n')
+            elif field['format'] == 'guint32-array':
+                inner_template = (
+                    '    guint32 *${field_name_underscore};\n')
+            elif field['format'] == 'guint64':
+                inner_template = (
+                    '    guint64 ${field_name_underscore};\n')
+            elif field['format'] == 'string':
+                inner_template = (
+                    '    gchar *${field_name_underscore};\n')
+            elif field['format'] == 'string-array':
+                inner_template = (
+                    '    gchar **${field_name_underscore};\n')
+            elif field['format'] == 'ipv4':
+                inner_template = (
+                    '    MbimIPv4 ${field_name_underscore};\n')
+            elif field['format'] == 'ref-ipv4':
+                inner_template = (
+                    '    MbimIPv4 ${field_name_underscore};\n')
+            elif field['format'] == 'ipv4-array':
+                inner_template = (
+                    '    MbimIPv4 *${field_name_underscore};\n')
+            elif field['format'] == 'ipv6':
+                inner_template = (
+                    '    MbimIPv6 ${field_name_underscore};\n')
+            elif field['format'] == 'ref-ipv6':
+                inner_template = (
+                    '    MbimIPv6 ${field_name_underscore};\n')
+            elif field['format'] == 'ipv6-array':
+                inner_template = (
+                    '    MbimIPv6 *${field_name_underscore};\n')
+            else:
+                raise ValueError('Cannot handle format \'%s\' in struct' % field['format'])
+            template += string.Template(inner_template).substitute(translations)
+        template += (
+            '} ${name};\n')
+        hfile.write(string.Template(template).substitute(translations))
+
+
+    """
+    Emit the type's free methods
+    """
+    def _emit_free(self, hfile, cfile):
+        translations = { 'name' : self.name,
+                         'name_underscore' : utils.build_underscore_name_from_camelcase(self.name) }
+        template = ''
+
+        if self.single_member == True:
+            template = (
+                '\n'
+                'void ${name_underscore}_free (${name} *var);\n')
+            hfile.write(string.Template(template).substitute(translations))
+
+
+        template = (
+            '\n'
+            'static void\n'
+            '_${name_underscore}_free (${name} *var)\n'
+            '{\n'
+            '    if (!var)\n'
+            '        return;\n'
+            '\n')
+
+        for field in self.contents:
+            translations['field_name_underscore'] = utils.build_underscore_name_from_camelcase(field['name'])
+            inner_template = ''
+            if field['format'] == 'uuid':
+                pass
+            elif field['format'] == 'unsized-byte-array' or field['format'] == 'ref-byte-array':
+                inner_template += (
+                    '    g_free (var->${field_name_underscore});\n')
+            elif field['format'] == 'guint32':
+                pass
+            elif field['format'] == 'guint32-array':
+                inner_template += (
+                    '    g_free (var->${field_name_underscore});\n')
+            elif field['format'] == 'guint64':
+                pass
+            elif field['format'] == 'string':
+                inner_template += (
+                    '    g_free (var->${field_name_underscore});\n')
+            elif field['format'] == 'string-array':
+                inner_template += (
+                    '    g_strfreev (var->${field_name_underscore});\n')
+            elif field['format'] == 'ipv4':
+                pass
+            elif field['format'] == 'ref-ipv4':
+                pass
+            elif field['format'] == 'ipv4-array':
+                inner_template += (
+                    '    g_free (var->${field_name_underscore});\n')
+            elif field['format'] == 'ipv6':
+                pass
+            elif field['format'] == 'ref-ipv6':
+                pass
+            elif field['format'] == 'ipv6-array':
+                inner_template += (
+                    '    g_free (var->${field_name_underscore});\n')
+            else:
+                raise ValueError('Cannot handle format \'%s\' in struct clear' % field['format'])
+            template += string.Template(inner_template).substitute(translations)
+
+        template += (
+            '    g_free (var);\n'
+            '}\n')
+        cfile.write(string.Template(template).substitute(translations))
+
+        if self.single_member == True:
+            template = (
+                '\n'
+                '/**\n'
+                ' * ${name_underscore}_free:\n'
+                ' * @var: a #${name}.\n'
+                ' *\n'
+                ' * Frees the memory allocated for the #${name}.\n'
+                ' */\n'
+                'void\n'
+                '${name_underscore}_free (${name} *var)\n'
+                '{\n'
+                '    _${name_underscore}_free (var);\n'
+                '}\n')
+            cfile.write(string.Template(template).substitute(translations))
+
+        if self.array_member:
+            template = (
+                '\n'
+                'void ${name_underscore}_array_free (${name} **array);\n')
+            hfile.write(string.Template(template).substitute(translations))
+
+            template = (
+                '\n'
+                '/**\n'
+                ' * ${name_underscore}_array_free:\n'
+                ' * @array: a #NULL terminated array of #${name} structs.\n'
+                ' *\n'
+                ' * Frees the memory allocated for the array of #${name}s.\n'
+                ' */\n'
+                'void\n'
+                '${name_underscore}_array_free (${name} **array)\n'
+                '{\n'
+                '    guint32 i;\n'
+                '\n'
+                '    if (!array)\n'
+                '        return;\n'
+                '\n'
+                '    for (i = 0; array[i]; i++)\n'
+                '        _${name_underscore}_free (array[i]);\n'
+                '    g_free (array);\n'
+                '}\n')
+            cfile.write(string.Template(template).substitute(translations))
+
+
+    """
+    Emit the type's read methods
+    """
+    def _emit_read(self, cfile):
+        translations = { 'name'            : self.name,
+                         'name_underscore' : utils.build_underscore_name_from_camelcase(self.name),
+                         'struct_size'     : self.size }
+
+        template = (
+            '\n'
+            'static ${name} *\n'
+            '_mbim_message_read_${name_underscore}_struct (\n'
+            '    const MbimMessage *self,\n'
+            '    guint32 relative_offset,\n'
+            '    guint32 *bytes_read)\n'
+            '{\n'
+            '    ${name} *out;\n'
+            '    guint32 offset = relative_offset;\n'
+            '\n'
+            '    g_assert (self != NULL);\n'
+            '\n'
+            '    out = g_new (${name}, 1);\n')
+
+        for field in self.contents:
+            translations['field_name_underscore'] = utils.build_underscore_name_from_camelcase(field['name'])
+
+            inner_template = ''
+            if field['format'] == 'uuid':
+                inner_template += (
+                    '\n'
+                    '    memcpy (&(out->${field_name_underscore}), _mbim_message_read_uuid (self, offset), 16);\n'
+                    '    offset += 16;\n')
+            elif field['format'] == 'ref-byte-array':
+                if 'array-size-field' in field:
+                    translations['array_size_field_name_underscore'] = utils.build_underscore_name_from_camelcase(field['array-size-field'])
+                    inner_template += (
+                        '\n'
+                        '    {\n'
+                        '        const guint8 *tmp;\n'
+                        '\n'
+                        '        tmp = _mbim_message_read_byte_array (self, relative_offset, offset, TRUE, FALSE, NULL);\n'
+                        '        out->${field_name_underscore} = g_malloc (out->${array_size_field_name_underscore});\n'
+                        '        memcpy (out->${field_name_underscore}, tmp, out->${array_size_field_name_underscore});\n'
+                        '        offset += 4;\n'
+                        '    }\n')
+                else:
+                    inner_template += (
+                        '\n'
+                        '    {\n'
+                        '        const guint8 *tmp;\n'
+                        '\n'
+                        '        tmp = _mbim_message_read_byte_array (self, relative_offset, offset, TRUE, TRUE, &(out->${field_name_underscore}_size));\n'
+                        '        out->${field_name_underscore} = g_malloc (out->${field_name_underscore}_size);\n'
+                        '        memcpy (out->${field_name_underscore}, tmp, out->${field_name_underscore}_size);\n'
+                        '        offset += 8;\n'
+                        '    }\n')
+            elif field['format'] == 'unsized-byte-array':
+                inner_template += (
+                    '\n'
+                    '    {\n'
+                    '        const guint8 *tmp;\n'
+                    '\n'
+                    '        tmp = _mbim_message_read_byte_array (self, relative_offset, offset, FALSE, FALSE, &(out->${field_name_underscore}_size));\n'
+                    '        out->${field_name_underscore} = g_malloc (out->${field_name_underscore}_size);\n'
+                    '        memcpy (out->${field_name_underscore}, tmp, out->${field_name_underscore}_size);\n'
+                    '        /* no offset update expected, this should be the last field */\n'
+                    '    }\n')
+            elif field['format'] == 'byte-array':
+                translations['array_size'] = field['array-size']
+                inner_template += (
+                    '\n'
+                    '    {\n'
+                    '        const guint8 *tmp;\n'
+                    '\n'
+                    '        tmp = _mbim_message_read_byte_array (self, relative_offset, offset, FALSE, FALSE, NULL));\n'
+                    '        memcpy (out->${field_name_underscore}, tmp, ${array_size});\n'
+                    '        offset += ${array_size};\n'
+                    '    }\n')
+            elif field['format'] == 'guint32':
+                inner_template += (
+                    '\n'
+                    '    out->${field_name_underscore} = _mbim_message_read_guint32 (self, offset);\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 += (
+                    '\n'
+                    '    out->${field_name_underscore} = _mbim_message_read_guint32_array (self, out->${array_size_field_name_underscore}, offset);\n'
+                    '    offset += (4 * out->${array_size_field_name_underscore});\n')
+            elif field['format'] == 'guint64':
+                inner_template += (
+                    '\n'
+                    '    out->${field_name_underscore} = _mbim_message_read_guint64 (self, offset);\n'
+                    '    offset += 8;\n')
+            elif field['format'] == 'string':
+                inner_template += (
+                    '\n'
+                    '    out->${field_name_underscore} = _mbim_message_read_string (self, relative_offset, offset);\n'
+                    '    offset += 8;\n')
+            elif field['format'] == 'string-array':
+                translations['array_size_field_name_underscore'] = utils.build_underscore_name_from_camelcase(field['array-size-field'])
+                inner_template += (
+                    '\n'
+                    '    out->${field_name_underscore} = _mbim_message_read_string_array (self, out->${array_size_field_name_underscore}, relative_offset, offset);\n'
+                    '    offset += (8 * out->${array_size_field_name_underscore});\n')
+            elif field['format'] == 'ipv4':
+                inner_template += (
+                    '\n'
+                    '    memcpy (&(out->${field_name_underscore}), _mbim_message_read_ipv4 (self, offset, FALSE), 4);\n'
+                    '    offset += 4;\n')
+            elif field['format'] == 'ref-ipv4':
+                inner_template += (
+                    '\n'
+                    '    memcpy (&(out->${field_name_underscore}), _mbim_message_read_ipv4 (self, offset, TRUE), 4);\n'
+                    '    offset += 4;\n')
+            elif field['format'] == 'ipv4-array':
+                inner_template += (
+                    '\n'
+                    '    out->${field_name_underscore} =_mbim_message_read_ipv4_array (self, out->${array_size_field_name_underscore}, offset);\n'
+                    '    offset += 4;\n')
+            elif field['format'] == 'ipv6':
+                inner_template += (
+                    '\n'
+                    '    memcpy (&(out->${field_name_underscore}), _mbim_message_read_ipv6 (self, offset, FALSE), 16);\n'
+                    '    offset += 4;\n')
+            elif field['format'] == 'ref-ipv6':
+                inner_template += (
+                    '\n'
+                    '    memcpy (&(out->${field_name_underscore}), _mbim_message_read_ipv6 (self, offset, TRUE), 16);\n'
+                    '    offset += 4;\n')
+            elif field['format'] == 'ipv6-array':
+                inner_template += (
+                    '\n'
+                    '    out->${field_name_underscore} =_mbim_message_read_ipv6_array (self, out->${array_size_field_name_underscore}, offset);\n'
+                    '    offset += 4;\n')
+            else:
+                raise ValueError('Cannot handle format \'%s\' in struct' % field['format'])
+
+            template += string.Template(inner_template).substitute(translations)
+
+        template += (
+            '\n'
+            '    if (bytes_read)\n'
+            '        *bytes_read = (offset - relative_offset);\n'
+            '\n'
+            '    return out;\n'
+            '}\n')
+        cfile.write(string.Template(template).substitute(translations))
+
+        template = (
+            '\n'
+            'static ${name} **\n'
+            '_mbim_message_read_${name_underscore}_struct_array (\n'
+            '    const MbimMessage *self,\n'
+            '    guint32 array_size,\n'
+            '    guint32 relative_offset_array_start,\n'
+            '    gboolean refs)\n'
+            '{\n'
+            '    ${name} **out;\n'
+            '    guint32 i;\n'
+            '    guint32 offset;\n'
+            '\n'
+            '    if (!array_size)\n'
+            '        return NULL;\n'
+            '\n'
+            '    out = g_new (${name} *, array_size + 1);\n'
+            '\n'
+            '    if (!refs) {\n'
+            '        offset = _mbim_message_read_guint32 (self, relative_offset_array_start);\n'
+            '        for (i = 0; i < array_size; i++, offset += ${struct_size})\n'
+            '            out[i] = _mbim_message_read_${name_underscore}_struct (self, offset, NULL);\n'
+            '    } else {\n'
+            '        offset = relative_offset_array_start;\n'
+            '        for (i = 0; i < array_size; i++, offset += 8)\n'
+            '            out[i] = _mbim_message_read_${name_underscore}_struct (self, _mbim_message_read_guint32 (self, offset), NULL);\n'
+            '    }\n'
+            '    out[array_size] = NULL;\n'
+            '\n'
+            '    return out;\n'
+            '}\n')
+        cfile.write(string.Template(template).substitute(translations))
+
+
+    """
+    Emit the type's append methods
+    """
+    def _emit_append(self, cfile):
+        translations = { 'name'            : self.name,
+                         'name_underscore' : utils.build_underscore_name_from_camelcase(self.name),
+                         'struct_size'     : self.size }
+
+        template = (
+            '\n'
+            'static GByteArray *\n'
+            '_${name_underscore}_struct_new (const ${name} *value)\n'
+            '{\n'
+            '    MbimStructBuilder *builder;\n'
+            '\n'
+            '    g_assert (value != NULL);\n'
+            '\n'
+            '    builder = _mbim_struct_builder_new ();\n')
+
+        for field in self.contents:
+            translations['field'] = utils.build_underscore_name_from_camelcase(field['name'])
+            translations['array_size'] = field['array-size'] if 'array-size' in field else ''
+            translations['array_size_field'] = utils.build_underscore_name_from_camelcase(field['array-size-field']) if 'array-size-field' in field else ''
+
+            if field['format'] == 'uuid':
+                inner_template = ('    _mbim_struct_builder_append_uuid (builder, &(value->${field}));\n')
+            elif field['format'] == 'byte-array':
+                inner_template = ('    _mbim_struct_builder_append_byte_array (builder, FALSE, FALSE, value->${field}, ${array_size});\n')
+            elif field['format'] == 'unsized-byte-array':
+                inner_template = ('    _mbim_struct_builder_append_byte_array (builder, FALSE, FALSE, value->${field}, value->${field}_size);\n')
+            elif field['format'] == 'ref-byte-array':
+                if 'array-size-field' in field:
+                    inner_template = ('    _mbim_struct_builder_append_byte_array (builder, TRUE, FALSE, value->${field}, value->${array_size_field});\n')
+                else:
+                    inner_template = ('    _mbim_struct_builder_append_byte_array (builder, TRUE, TRUE, value->${field}, value->${field}_size);\n')
+            elif field['format'] == 'guint32':
+                inner_template = ('    _mbim_struct_builder_append_guint32 (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':
+                inner_template = ('    _mbim_struct_builder_append_guint64 (builder, value->${field});\n')
+            elif field['format'] == 'guint64-array':
+                inner_template = ('    _mbim_struct_builder_append_guint64_array (builder, value->${field}, value->${array_size_field});\n')
+            elif field['format'] == 'string':
+                inner_template = ('    _mbim_struct_builder_append_string (builder, value->${field});\n')
+            elif field['format'] == 'string-array':
+                inner_template = ('    _mbim_struct_builder_append_string_array (builder, value->${field}, value->${array_size_field});\n')
+            elif field['format'] == 'ipv4':
+                inner_template = ('    _mbim_struct_builder_append_ipv4 (builder, &value->${field}, FALSE);\n')
+            elif field['format'] == 'ref-ipv4':
+                inner_template = ('    _mbim_struct_builder_append_ipv4 (builder, &value->${field}, TRUE);\n')
+            elif field['format'] == 'ipv4-array':
+                inner_template = ('    _mbim_struct_builder_append_ipv4_array (builder, value->${field}, value->${array_size_field});\n')
+            elif field['format'] == 'ipv6':
+                inner_template = ('    _mbim_struct_builder_append_ipv6 (builder, &value->${field}, FALSE);\n')
+            elif field['format'] == 'ref-ipv6':
+                inner_template = ('    _mbim_struct_builder_append_ipv6 (builder, &value->${field}, TRUE);\n')
+            elif field['format'] == 'ipv6-array':
+                inner_template = ('    _mbim_struct_builder_append_ipv6_array (builder, value->${field}, value->${array_size_field});\n')
+            else:
+                raise ValueError('Cannot handle format \'%s\' in struct' % field['format'])
+
+            template += string.Template(inner_template).substitute(translations)
+
+        template += (
+            '\n'
+            '    return _mbim_struct_builder_complete (builder);\n'
+            '}\n')
+        cfile.write(string.Template(template).substitute(translations))
+
+        template = (
+            '\n'
+            'static void\n'
+            '_mbim_struct_builder_append_${name_underscore}_struct (\n'
+            '    MbimStructBuilder *builder,\n'
+            '    const ${name} *value)\n'
+            '{\n'
+            '    GByteArray *raw;\n'
+            '\n'
+            '    raw = _${name_underscore}_struct_new (value);\n'
+            '    g_byte_array_append (builder->fixed_buffer, raw->data, raw->len);\n'
+            '    g_byte_array_unref (raw);\n'
+            '}\n'
+            '\n'
+            'static void\n'
+            '_mbim_message_command_builder_append_${name_underscore}_struct (\n'
+            '    MbimMessageCommandBuilder *builder,\n'
+            '    const ${name} *value)\n'
+            '{\n'
+            '    _mbim_struct_builder_append_${name_underscore}_struct (builder->contents_builder, value);\n'
+            '}\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))
+
+        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))
+
+
+    """
+    Emit the struct handling implementation
+    """
+    def emit(self, hfile, cfile):
+        utils.add_separator(hfile, 'Struct', self.name);
+        utils.add_separator(cfile, 'Struct', self.name);
+
+        # Emit type
+        self._emit_type(hfile)
+        # Emit type's free
+        self._emit_free(hfile, cfile)
+        # Emit type's read
+        self._emit_read(cfile)
+        # Emit type's append
+        self._emit_append(cfile)
+
+
+    """
+    Emit the section contents
+    """
+    def emit_section_content(self, sfile):
+        translations = { 'struct_name'     : self.name,
+                         'name_underscore' : utils.build_underscore_name_from_camelcase(self.name) }
+        template = (
+            '<SUBSECTION ${struct_name}>\n'
+            '${struct_name}\n')
+        if self.single_member == True:
+            template += (
+                '${name_underscore}_free\n')
+        if self.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
new file mode 100755
index 0000000..4d01384
--- /dev/null
+++ b/build-aux/mbim-codegen/mbim-codegen
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+# -*- 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
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+#
+
+import os
+import sys
+import optparse
+import json
+
+from ObjectList import ObjectList
+import utils
+
+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:
+        raise RuntimeError('Input JSON file is mandatory')
+    if opts.output == None:
+        raise RuntimeError('Output file pattern is mandatory')
+
+    # Prepare output file names
+    output_file_c = open(opts.output + ".c", 'w')
+    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)
+    object_list = ObjectList(object_list_json)
+
+    # Add common stuff to the output files
+    utils.add_copyright(output_file_c);
+    utils.add_copyright(output_file_h);
+    utils.add_header_start(output_file_h, os.path.basename(opts.output))
+    utils.add_source_start(output_file_c, os.path.basename(opts.output))
+
+    # Emit the message creation/parsing code
+    object_list.emit(output_file_h, output_file_c)
+
+    # Emit sections
+    object_list.emit_sections(output_file_sections)
+
+    utils.add_header_stop(output_file_h, os.path.basename(opts.output))
+
+    output_file_c.close()
+    output_file_h.close()
+    output_file_sections.close()
+
+    sys.exit(0)
+
+
+if __name__ == "__main__":
+    codegen_main()
diff --git a/build-aux/mbim-codegen/utils.py b/build-aux/mbim-codegen/utils.py
new file mode 100644
index 0000000..7666a1d
--- /dev/null
+++ b/build-aux/mbim-codegen/utils.py
@@ -0,0 +1,192 @@
+#!/usr/bin/env python
+# -*- 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
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2012 Lanedo GmbH
+# Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+#
+# Implementation originally developed in 'libqmi'.
+#
+
+import string
+import re
+
+"""
+Add the common copyright header to the given file
+"""
+def add_copyright(f):
+    f.write(
+        "\n"
+        "/* GENERATED CODE... DO NOT EDIT */\n"
+        "\n"
+        "/*\n"
+        " * This library is free software; you can redistribute it and/or\n"
+        " * modify it under the terms of the GNU Lesser General Public\n"
+        " * License as published by the Free Software Foundation; either\n"
+        " * version 2 of the License, or (at your option) any later version.\n"
+        " *\n"
+        " * This library is distributed in the hope that it will be useful,\n"
+        " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+        " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n"
+        " * Lesser General Public License for more details.\n"
+        " *\n"
+        " * You should have received a copy of the GNU Lesser General Public\n"
+        " * License along with this library; if not, write to the\n"
+        " * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,\n"
+        " * Boston, MA 02110-1301 USA.\n"
+        " *\n"
+        " * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>\n"
+        " */\n"
+        "\n");
+
+
+"""
+Build a header guard string based on the given filename
+"""
+def build_header_guard(output_name):
+    return "__LIBMBIM_GLIB_" + output_name.replace('-', '_').upper() + "__"
+
+"""
+Write the common header start chunk
+"""
+def add_header_start(f, output_name):
+    translations = { 'guard'   : build_header_guard(output_name) }
+    template = (
+        "\n"
+        "#include <glib.h>\n"
+        "#include <glib-object.h>\n"
+        "#include <gio/gio.h>\n"
+        "\n"
+        "#include \"mbim-message.h\"\n"
+        "#include \"mbim-device.h\"\n"
+        "#include \"mbim-enums.h\"\n"
+        "\n"
+        "#ifndef ${guard}\n"
+        "#define ${guard}\n"
+        "\n"
+        "G_BEGIN_DECLS\n")
+    f.write(string.Template(template).substitute(translations))
+
+
+"""
+Write the common header stop chunk
+"""
+def add_header_stop(f, output_name):
+    template = string.Template (
+        "\n"
+        "G_END_DECLS\n"
+        "\n"
+        "#endif /* ${guard} */\n")
+    f.write(template.substitute(guard = build_header_guard(output_name)))
+
+
+"""
+Write the common source file start chunk
+"""
+def add_source_start(f, output_name):
+    template = string.Template (
+        "\n"
+        "#include <string.h>\n"
+        "\n"
+        "#include \"${name}.h\"\n"
+        "#include \"mbim-message-private.h\"\n"
+        "#include \"mbim-enum-types.h\"\n"
+        "#include \"mbim-error-types.h\"\n"
+        "#include \"mbim-device.h\"\n"
+        "#include \"mbim-utils.h\"\n")
+    f.write(template.substitute(name = output_name))
+
+
+"""
+Write a separator comment in the file
+"""
+def add_separator(f, separator_type, separator_name):
+    template = string.Template (
+        "\n"
+        "/*****************************************************************************/\n"
+        "/* ${type}: ${name} */\n")
+    f.write(template.substitute(type = separator_type,
+                                name = separator_name))
+
+
+"""
+Build an underscore name from the given full name
+e.g.: "This is a message" --> "this_is_a_message"
+"""
+def build_underscore_name(name):
+    return name.replace(' ', '_').replace('-', '_').lower()
+
+
+"""
+Build an underscore uppercase name from the given full name
+e.g.: "This is a message" --> "THIS_IS_A_MESSAGE"
+"""
+def build_underscore_uppercase_name(name):
+    return name.replace(' ', '_').replace('-', '_').upper()
+
+
+
+"""
+Build an underscore name from the given camelcase name
+e.g.: "ThisIsAMessage" --> "this_is_a_message"
+"""
+def build_underscore_name_from_camelcase(camelcase):
+    s0 = camelcase.replace('IP','Ip')
+    s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', s0)
+    return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
+
+
+
+"""
+Build a camelcase name from the given full name
+e.g.: "This is a message" --> "ThisIsAMessage"
+"""
+def build_camelcase_name(name):
+    return string.capwords(name).replace(' ', '')
+
+
+"""
+Build a dashed lowercase name from the given full name
+e.g.: "This is a message" --> "this-is-a-message"
+"""
+def build_dashed_name(name):
+    return name.lower().replace(' ', '-')
+
+
+"""
+Remove the given prefix from the string
+"""
+def remove_prefix(line, prefix):
+    return line[len(prefix):] if line.startswith(prefix) else line
+
+
+"""
+Read the contents of the JSON file, skipping lines prefixed with '//', which are
+considered comments.
+"""
+def read_json_file(path):
+    f = open(path)
+    out = ''
+    for line in f.readlines():
+        stripped = line.strip()
+        if stripped.startswith('//'):
+            # Skip this line
+            # We add an empty line instead so that errors when parsing the JSON
+            # report the proper line number
+            out += "\n"
+        else:
+            out += line
+    return out
diff --git a/build-aux/templates/Makefile.am b/build-aux/templates/Makefile.am
new file mode 100644
index 0000000..d663e1d
--- /dev/null
+++ b/build-aux/templates/Makefile.am
@@ -0,0 +1,7 @@
+
+EXTRA_DIST = \
+	mbim-error-types-template.h \
+	mbim-error-types-template.c \
+	mbim-error-quarks-template.c \
+	mbim-enum-types-template.h \
+	mbim-enum-types-template.c
diff --git a/build-aux/templates/mbim-enum-types-template.c b/build-aux/templates/mbim-enum-types-template.c
new file mode 100644
index 0000000..635147f
--- /dev/null
+++ b/build-aux/templates/mbim-enum-types-template.c
@@ -0,0 +1,118 @@
+/*** BEGIN file-header ***/
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+static const G@Type@Value @enum_name@_values[] = {
+/*** END value-header ***/
+/*** BEGIN value-production ***/
+    { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+/*** BEGIN value-tail ***/
+    { 0, NULL, NULL }
+};
+
+/* Define type-specific symbols */
+
+GType
+@enum_name@_get_type (void)
+{
+    static volatile gsize g_define_type_id__volatile = 0;
+
+    if (g_once_init_enter (&g_define_type_id__volatile)) {
+        GType g_define_type_id =
+            g_@type@_register_static (g_intern_static_string ("@EnumName@"),
+                                      @enum_name@_values);
+        g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+    return g_define_type_id__volatile;
+}
+
+/* Enum-specific method to get the value as a string.
+ * We get the nick of the GEnumValue. Note that this will be
+ * valid even if the GEnumClass is not referenced anywhere. */
+#if defined __@ENUMNAME@_IS_ENUM__
+/**
+ * @enum_name@_get_string:
+ * @val: a @EnumName@.
+ *
+ * Gets the nickname string for the #@EnumName@ specified at @val.
+ *
+ * Returns: (transfer none): a string with the nickname, or %NULL if not found. Do not free the returned value.
+ */
+const gchar *
+@enum_name@_get_string (@EnumName@ val)
+{
+    guint i;
+
+    for (i = 0; @enum_name@_values[i].value_nick; i++) {
+        if (val == @enum_name@_values[i].value)
+            return @enum_name@_values[i].value_nick;
+    }
+
+    return NULL;
+}
+#endif /* __@ENUMNAME@_IS_ENUM__ */
+
+/* Flags-specific method to build a string with the given mask.
+ * We get a comma separated list of the nicks of the GFlagsValues.
+ * Note that this will be valid even if the GFlagsClass is not referenced
+ * anywhere. */
+#if defined __@ENUMNAME@_IS_FLAGS__
+/**
+ * @enum_name@_build_string_from_mask:
+ * @mask: bitmask of @EnumName@ values.
+ *
+ * Builds a string containing a comma-separated list of nicknames for
+ * each #@EnumName@ in @mask.
+ *
+ * Returns: (transfer full): a string with the list of nicknames, or %NULL if none given. The returned value should be freed with g_free().
+ */
+gchar *
+@enum_name@_build_string_from_mask (@EnumName@ mask)
+{
+    guint i;
+    gboolean first = TRUE;
+    GString *str = NULL;
+
+    for (i = 0; @enum_name@_values[i].value_nick; i++) {
+        /* We also look for exact matches */
+        if (mask == @enum_name@_values[i].value) {
+            if (str)
+                g_string_free (str, TRUE);
+            return g_strdup (@enum_name@_values[i].value_nick);
+        }
+
+        /* Build list with single-bit masks */
+        if (mask & @enum_name@_values[i].value) {
+            guint c;
+            gulong number = @enum_name@_values[i].value;
+
+            for (c = 0; number; c++)
+                number &= number - 1;
+
+            if (c == 1) {
+                if (!str)
+                    str = g_string_new ("");
+                g_string_append_printf (str, "%s%s",
+                                        first ? "" : ", ",
+                                        @enum_name@_values[i].value_nick);
+                if (first)
+                    first = FALSE;
+            }
+        }
+    }
+
+    return (str ? g_string_free (str, FALSE) : NULL);
+}
+#endif /* __@ENUMNAME@_IS_FLAGS__ */
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+/*** END file-tail ***/
diff --git a/build-aux/templates/mbim-enum-types-template.h b/build-aux/templates/mbim-enum-types-template.h
new file mode 100644
index 0000000..31e0732
--- /dev/null
+++ b/build-aux/templates/mbim-enum-types-template.h
@@ -0,0 +1,33 @@
+/*** BEGIN file-header ***/
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
+
+/* Define type-specific symbols */
+#define __@ENUMNAME@_IS_@TYPE@__
+
+#if defined __@ENUMNAME@_IS_ENUM__
+const gchar *@enum_name@_get_string (@EnumName@ val);
+#endif
+
+#if defined __@ENUMNAME@_IS_FLAGS__
+gchar *@enum_name@_build_string_from_mask (@EnumName@ mask);
+#endif
+
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+/*** END file-tail ***/
diff --git a/build-aux/templates/mbim-error-quarks-template.c b/build-aux/templates/mbim-error-quarks-template.c
new file mode 100644
index 0000000..4386a60
--- /dev/null
+++ b/build-aux/templates/mbim-error-quarks-template.c
@@ -0,0 +1,42 @@
+/*** BEGIN file-header ***/
+
+#include <gio/gio.h>
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+
+#define ERROR_PREFIX @ENUMNAME@_DBUS_PREFIX
+static const GDBusErrorEntry @enum_name@_entries[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+    { @VALUENAME@, ERROR_PREFIX ".@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+};
+#undef ERROR_PREFIX
+
+GQuark
+@enum_name@_quark (void)
+{
+    static volatile gsize quark_volatile = 0;
+
+    if (!quark_volatile)
+        g_dbus_error_register_error_domain ("@enum_name@_quark",
+                                            &quark_volatile,
+                                            @enum_name@_entries,
+                                            G_N_ELEMENTS (@enum_name@_entries));
+
+    return (GQuark) quark_volatile;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+/*** END file-tail ***/
diff --git a/build-aux/templates/mbim-error-types-template.c b/build-aux/templates/mbim-error-types-template.c
new file mode 100644
index 0000000..66ab892
--- /dev/null
+++ b/build-aux/templates/mbim-error-types-template.c
@@ -0,0 +1,62 @@
+/*** BEGIN file-header ***/
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+static const G@Type@Value @enum_name@_values[] = {
+/*** END value-header ***/
+/*** BEGIN value-production ***/
+    { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+/*** BEGIN value-tail ***/
+    { 0, NULL, NULL }
+};
+
+/* @enum_name@_quark() implemented in mbim-errors-quarks.c */
+
+GType
+@enum_name@_get_type (void)
+{
+    static volatile gsize g_define_type_id__volatile = 0;
+
+    if (g_once_init_enter (&g_define_type_id__volatile)) {
+        GType g_define_type_id =
+            g_@type@_register_static (g_intern_static_string ("@EnumName@"),
+                                      @enum_name@_values);
+        g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+    return g_define_type_id__volatile;
+}
+
+/* Enum-specific method to get the value as a string.
+ * We get the nick of the GEnumValue. Note that this will be
+ * valid even if the GEnumClass is not referenced anywhere. */
+/**
+ * @enum_name@_get_string:
+ * @val: a @EnumName@.
+ *
+ * Gets the nickname string for the #@EnumName@ specified at @val.
+ *
+ * Returns: (transfer none): a string with the nickname, or %NULL if not found. Do not free the returned value.
+ */
+const gchar *
+@enum_name@_get_string (@EnumName@ val)
+{
+    guint i;
+
+    for (i = 0; @enum_name@_values[i].value_nick; i++) {
+        if (val == @enum_name@_values[i].value)
+            return @enum_name@_values[i].value_nick;
+    }
+
+    return NULL;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+/*** END file-tail ***/
diff --git a/build-aux/templates/mbim-error-types-template.h b/build-aux/templates/mbim-error-types-template.h
new file mode 100644
index 0000000..217f006
--- /dev/null
+++ b/build-aux/templates/mbim-error-types-template.h
@@ -0,0 +1,24 @@
+/*** BEGIN file-header ***/
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GQuark @enum_name@_quark    (void);
+GType  @enum_name@_get_type (void) G_GNUC_CONST;
+#define @ENUMNAME@ (@enum_name@_quark ())
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
+const gchar *@enum_name@_get_string (@EnumName@ val);
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+/*** END file-tail ***/
diff --git a/cli/Makefile.am b/cli/Makefile.am
new file mode 100644
index 0000000..3685e93
--- /dev/null
+++ b/cli/Makefile.am
@@ -0,0 +1,19 @@
+
+bin_PROGRAMS = mbimcli
+
+mbimcli_CPPFLAGS = \
+	$(MBIMCLI_CFLAGS) \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/libmbim-glib \
+	-I$(top_builddir)/libmbim-glib \
+	-I$(top_srcdir)/libmbim-glib/generated \
+	-I$(top_builddir)/libmbim-glib/generated
+
+mbimcli_SOURCES = \
+	mbimcli.h \
+	mbimcli.c \
+	mbimcli-basic-connect.c
+
+mbimcli_LDADD = \
+	$(MBIMCLI_LIBS) \
+	$(top_builddir)/libmbim-glib/libmbim-glib.la
diff --git a/cli/mbimcli-basic-connect.c b/cli/mbimcli-basic-connect.c
new file mode 100644
index 0000000..8f2db5e
--- /dev/null
+++ b/cli/mbimcli-basic-connect.c
@@ -0,0 +1,1715 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * mbimcli -- Command line interface to control MBIM devices
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libmbim-glib.h>
+
+#include "mbimcli.h"
+
+/* Context */
+typedef struct {
+    MbimDevice *device;
+    GCancellable *cancellable;
+} Context;
+static Context *ctx;
+
+/* Options */
+static gboolean  query_device_caps_flag;
+static gboolean  query_subscriber_ready_status_flag;
+static gboolean  query_radio_state_flag;
+static gboolean  query_device_services_flag;
+static gboolean  query_pin_flag;
+static gchar    *set_pin_enter_str;
+static gchar    *set_pin_change_str;
+static gchar    *set_pin_enable_str;
+static gchar    *set_pin_disable_str;
+static gchar    *set_pin_enter_puk_str;
+static gboolean  query_home_provider_flag;
+static gboolean  query_preferred_providers_flag;
+static gboolean  query_visible_providers_flag;
+static gboolean  query_register_state_flag;
+static gboolean  set_register_state_automatic_flag;
+static gboolean  query_signal_state_flag;
+static gboolean  query_packet_service_flag;
+static gboolean  set_packet_service_attach_flag;
+static gboolean  set_packet_service_detach_flag;
+static gboolean  query_connect_flag;
+static gchar    *set_connect_activate_str;
+static gboolean  set_connect_deactivate_flag;
+static gboolean  query_packet_statistics_flag;
+
+static GOptionEntry entries[] = {
+    { "query-device-caps", 0, 0, G_OPTION_ARG_NONE, &query_device_caps_flag,
+      "Query device capabilities",
+      NULL
+    },
+    { "query-subscriber-ready-status", 0, 0, G_OPTION_ARG_NONE, &query_subscriber_ready_status_flag,
+      "Query subscriber ready status",
+      NULL
+    },
+    { "query-radio-state", 0, 0, G_OPTION_ARG_NONE, &query_radio_state_flag,
+      "Query radio state",
+      NULL
+    },
+    { "query-device-services", 0, 0, G_OPTION_ARG_NONE, &query_device_services_flag,
+      "Query device services",
+      NULL
+    },
+    { "query-pin-state", 0, 0, G_OPTION_ARG_NONE, &query_pin_flag,
+      "Query PIN state",
+      NULL
+    },
+    { "enter-pin", 0, 0, G_OPTION_ARG_STRING, &set_pin_enter_str,
+      "Enter PIN",
+      "[(current PIN)]"
+    },
+    { "change-pin", 0, 0, G_OPTION_ARG_STRING, &set_pin_change_str,
+      "Change PIN",
+      "[(current PIN),(new PIN)]"
+    },
+    { "enable-pin", 0, 0, G_OPTION_ARG_STRING, &set_pin_enable_str,
+      "Enable PIN",
+      "[(current PIN)]"
+    },
+    { "disable-pin", 0, 0, G_OPTION_ARG_STRING, &set_pin_disable_str,
+      "Disable PIN",
+      "[(current PIN)]"
+    },
+    { "enter-puk", 0, 0, G_OPTION_ARG_STRING, &set_pin_enter_puk_str,
+      "Enter PUK",
+      "[(PUK),(new PIN)]"
+    },
+    { "query-home-provider", 0, 0, G_OPTION_ARG_NONE, &query_home_provider_flag,
+      "Query home provider",
+      NULL
+    },
+    { "query-preferred-providers", 0, 0, G_OPTION_ARG_NONE, &query_preferred_providers_flag,
+      "Query preferred providers",
+      NULL
+    },
+    { "query-visible-providers", 0, 0, G_OPTION_ARG_NONE, &query_visible_providers_flag,
+      "Query visible providers",
+      NULL
+    },
+    { "query-registration-state", 0, 0, G_OPTION_ARG_NONE, &query_register_state_flag,
+      "Query registration state",
+      NULL
+    },
+    { "register-automatic", 0, 0, G_OPTION_ARG_NONE, &set_register_state_automatic_flag,
+      "Launch automatic registration",
+      NULL
+    },
+    { "query-signal-state", 0, 0, G_OPTION_ARG_NONE, &query_signal_state_flag,
+      "Query signal state",
+      NULL
+    },
+    { "query-packet-service-state", 0, 0, G_OPTION_ARG_NONE, &query_packet_service_flag,
+      "Query packet service state",
+      NULL
+    },
+    { "attach-packet-service", 0, 0, G_OPTION_ARG_NONE, &set_packet_service_attach_flag,
+      "Attach to the packet service",
+      NULL
+    },
+    { "detach-packet-service", 0, 0, G_OPTION_ARG_NONE, &set_packet_service_detach_flag,
+      "Detach from the packet service",
+      NULL
+    },
+    { "query-connection-state", 0, 0, G_OPTION_ARG_NONE, &query_connect_flag,
+      "Query connection state",
+      NULL
+    },
+    { "connect", 0, 0, G_OPTION_ARG_STRING, &set_connect_activate_str,
+      "Connect (Authentication, Username and Password are optional)",
+      "[(APN),(PAP|CHAP|MSCHAPV2),(Username),(Password)]"
+    },
+    { "disconnect", 0, 0, G_OPTION_ARG_NONE, &set_connect_deactivate_flag,
+      "Disconnect",
+      NULL
+    },
+    { "query-packet-statistics", 0, 0, G_OPTION_ARG_NONE, &query_packet_statistics_flag,
+      "Query packet statistics",
+      NULL
+    },
+    { NULL }
+};
+
+GOptionGroup *
+mbimcli_basic_connect_get_option_group (void)
+{
+	GOptionGroup *group;
+
+	group = g_option_group_new ("basic-connect",
+	                            "Basic Connect options",
+	                            "Show Basic Connect Service options",
+	                            NULL,
+	                            NULL);
+	g_option_group_add_entries (group, entries);
+
+	return group;
+}
+
+gboolean
+mbimcli_basic_connect_options_enabled (void)
+{
+    static guint n_actions = 0;
+    static gboolean checked = FALSE;
+
+    if (checked)
+        return !!n_actions;
+
+    n_actions = (query_device_caps_flag +
+                 query_subscriber_ready_status_flag +
+                 query_radio_state_flag +
+                 query_device_services_flag +
+                 query_pin_flag +
+                 !!set_pin_enter_str +
+                 !!set_pin_change_str +
+                 !!set_pin_enable_str +
+                 !!set_pin_disable_str +
+                 !!set_pin_enter_puk_str +
+                 query_register_state_flag +
+                 query_home_provider_flag +
+                 query_preferred_providers_flag +
+                 query_visible_providers_flag +
+                 set_register_state_automatic_flag +
+                 query_signal_state_flag +
+                 query_packet_service_flag +
+                 set_packet_service_attach_flag +
+                 set_packet_service_detach_flag +
+                 query_connect_flag +
+                 !!set_connect_activate_str +
+                 set_connect_deactivate_flag +
+                 query_packet_statistics_flag);
+
+    if (n_actions > 1) {
+        g_printerr ("error: too many Basic Connect actions requested\n");
+        exit (EXIT_FAILURE);
+    }
+
+    checked = TRUE;
+    return !!n_actions;
+}
+
+static void
+context_free (Context *context)
+{
+    if (!context)
+        return;
+
+    if (context->cancellable)
+        g_object_unref (context->cancellable);
+    if (context->device)
+        g_object_unref (context->device);
+    g_slice_free (Context, context);
+}
+
+static void
+shutdown (gboolean operation_status)
+{
+    /* Cleanup context and finish async operation */
+    context_free (ctx);
+    mbimcli_async_operation_done (operation_status);
+}
+
+static void
+query_device_caps_ready (MbimDevice   *device,
+                         GAsyncResult *res)
+{
+    MbimMessage *response;
+    GError *error = NULL;
+    MbimDeviceType device_type;
+    const gchar *device_type_str;
+    MbimCellularClass cellular_class;
+    gchar *cellular_class_str;
+    MbimVoiceClass voice_class;
+    const gchar *voice_class_str;
+    MbimSimClass sim_class;
+    gchar *sim_class_str;
+    MbimDataClass data_class;
+    gchar *data_class_str;
+    MbimSmsCaps sms_caps;
+    gchar *sms_caps_str;
+    MbimCtrlCaps ctrl_caps;
+    gchar *ctrl_caps_str;
+    guint32 max_sessions;
+    gchar *custom_data_class;
+    gchar *device_id;
+    gchar *firmware_info;
+    gchar *hardware_info;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    if (!mbim_message_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,
+            &error)) {
+        g_printerr ("error: couldn't parse response message: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    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_device_type_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);
+
+#undef VALIDATE_UNKNOWN
+#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
+
+    g_print ("[%s] Device capabilities retrieved:\n"
+             "\t      Device type: '%s'\n"
+             "\t   Cellular class: '%s'\n"
+             "\t      Voice class: '%s'\n"
+             "\t        Sim class: '%s'\n"
+             "\t       Data class: '%s'\n"
+             "\t         SMS caps: '%s'\n"
+             "\t        Ctrl caps: '%s'\n"
+             "\t     Max sessions: '%u'\n"
+             "\tCustom data class: '%s'\n"
+             "\t        Device ID: '%s'\n"
+             "\t    Firmware info: '%s'\n"
+             "\t    Hardware info: '%s'\n",
+             mbim_device_get_path_display (device),
+             VALIDATE_UNKNOWN (device_type_str),
+             VALIDATE_UNKNOWN (cellular_class_str),
+             VALIDATE_UNKNOWN (voice_class_str),
+             VALIDATE_UNKNOWN (sim_class_str),
+             VALIDATE_UNKNOWN (data_class_str),
+             VALIDATE_UNKNOWN (sms_caps_str),
+             VALIDATE_UNKNOWN (ctrl_caps_str),
+             max_sessions,
+             VALIDATE_UNKNOWN (custom_data_class),
+             VALIDATE_UNKNOWN (device_id),
+             VALIDATE_UNKNOWN (firmware_info),
+             VALIDATE_UNKNOWN (hardware_info));
+
+    g_free (cellular_class_str);
+    g_free (sim_class_str);
+    g_free (data_class_str);
+    g_free (sms_caps_str);
+    g_free (ctrl_caps_str);
+    g_free (custom_data_class);
+    g_free (device_id);
+    g_free (firmware_info);
+    g_free (hardware_info);
+
+    mbim_message_unref (response);
+    shutdown (TRUE);
+}
+
+static void
+query_subscriber_ready_status_ready (MbimDevice   *device,
+                                     GAsyncResult *res)
+{
+    MbimMessage *response;
+    GError *error = NULL;
+    MbimSubscriberReadyState ready_state;
+    const gchar *ready_state_str;
+    gchar *subscriber_id;
+    gchar *sim_iccid;
+    MbimReadyInfoFlag ready_info;
+    gchar *ready_info_str;
+    guint32 telephone_numbers_count;
+    gchar **telephone_numbers;
+    gchar *telephone_numbers_str;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        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);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    telephone_numbers_str = (telephone_numbers ? g_strjoinv (", ", telephone_numbers) : NULL);
+    ready_state_str = mbim_subscriber_ready_state_get_string (ready_state);
+    ready_info_str = mbim_ready_info_flag_build_string_from_mask (ready_info);
+
+#undef VALIDATE_UNKNOWN
+#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
+
+    g_print ("[%s] Subscriber ready status retrieved:\n"
+             "\t      Ready state: '%s'\n"
+             "\t    Subscriber ID: '%s'\n"
+             "\t        SIM ICCID: '%s'\n"
+             "\t       Ready info: '%s'\n"
+             "\tTelephone numbers: (%u) '%s'\n",
+             mbim_device_get_path_display (device),
+             VALIDATE_UNKNOWN (ready_state_str),
+             VALIDATE_UNKNOWN (subscriber_id),
+             VALIDATE_UNKNOWN (sim_iccid),
+             VALIDATE_UNKNOWN (ready_info_str),
+             telephone_numbers_count, VALIDATE_UNKNOWN (telephone_numbers_str));
+
+    g_free (subscriber_id);
+    g_free (sim_iccid);
+    g_free (ready_info_str);
+    g_strfreev (telephone_numbers);
+    g_free (telephone_numbers_str);
+
+    mbim_message_unref (response);
+    shutdown (TRUE);
+}
+
+static void
+query_radio_state_ready (MbimDevice   *device,
+                         GAsyncResult *res)
+{
+    MbimMessage *response;
+    GError *error = NULL;
+    MbimRadioSwitchState hardware_radio_state;
+    const gchar *hardware_radio_state_str;
+    MbimRadioSwitchState software_radio_state;
+    const gchar *software_radio_state_str;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    if (!mbim_message_radio_state_response_parse (
+            response,
+            &hardware_radio_state,
+            &software_radio_state,
+            &error)) {
+        g_printerr ("error: couldn't parse response message: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    hardware_radio_state_str = mbim_radio_switch_state_get_string (hardware_radio_state);
+    software_radio_state_str = mbim_radio_switch_state_get_string (software_radio_state);
+
+#undef VALIDATE_UNKNOWN
+#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
+
+    g_print ("[%s] Radio state retrieved:\n"
+             "\t     Hardware Radio State: '%s'\n"
+             "\t     Software Radio State: '%s'\n",
+             mbim_device_get_path_display (device),
+             VALIDATE_UNKNOWN (hardware_radio_state_str),
+             VALIDATE_UNKNOWN (software_radio_state_str));
+
+    mbim_message_unref (response);
+    shutdown (TRUE);
+}
+
+static void
+query_device_services_ready (MbimDevice   *device,
+                             GAsyncResult *res)
+{
+    MbimMessage *response;
+    GError *error = NULL;
+    MbimDeviceServiceElement **device_services;
+    guint32 device_services_count;
+    guint32 max_dss_sessions;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    if (!mbim_message_device_services_response_parse (
+            response,
+            &device_services_count,
+            &max_dss_sessions,
+            &device_services,
+            &error)) {
+        g_printerr ("error: couldn't parse response message: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    g_print ("[%s] Device services retrieved:\n"
+             "\tMax DSS sessions: '%u'\n",
+             mbim_device_get_path_display (device),
+             max_dss_sessions);
+    if (device_services_count == 0)
+        g_print ("\t        Services: None\n");
+    else {
+        guint32 i;
+
+        g_print ("\t        Services: (%u)\n", device_services_count);
+        for (i = 0; i < device_services_count; i++) {
+            MbimService service;
+            gchar *uuid_str;
+            GString *cids;
+            guint32 j;
+
+            service = mbim_uuid_to_service (&device_services[i]->device_service_id);
+            uuid_str = mbim_uuid_get_printable (&device_services[i]->device_service_id);
+
+            cids = g_string_new ("");
+            for (j = 0; j < device_services[i]->cids_count; j++) {
+                if (service == MBIM_SERVICE_INVALID) {
+                    g_string_append_printf (cids, "%u", device_services[i]->cids[j]);
+                    if (j < device_services[i]->cids_count - 1)
+                        g_string_append (cids, ", ");
+                } else {
+                    g_string_append_printf (cids, "%s%s (%u)",
+                                            j == 0 ? "" : "\t\t                   ",
+                                            mbim_cid_get_printable (service, device_services[i]->cids[j]),
+                                            device_services[i]->cids[j]);
+                    if (j < device_services[i]->cids_count - 1)
+                        g_string_append (cids, ",\n");
+                }
+            }
+
+            g_print ("\n"
+                     "\t\t          Service: '%s'\n"
+                     "\t\t             UUID: [%s]:\n"
+                     "\t\t      DSS payload: %u\n"
+                     "\t\tMax DSS instances: %u\n"
+                     "\t\t             CIDs: %s\n",
+                     service == MBIM_SERVICE_INVALID ? "unknown" : mbim_service_get_string (service),
+                     uuid_str,
+                     device_services[i]->dss_payload,
+                     device_services[i]->max_dss_instances,
+                     cids->str);
+
+            g_string_free (cids, TRUE);
+            g_free (uuid_str);
+        }
+    }
+
+    mbim_device_service_element_array_free (device_services);
+
+    mbim_message_unref (response);
+    shutdown (TRUE);
+}
+
+static void
+pin_ready (MbimDevice   *device,
+           GAsyncResult *res,
+           gpointer user_data)
+{
+    MbimMessage *response;
+    GError *error = NULL;
+    MbimPinType pin_type;
+    MbimPinState pin_state;
+    const gchar *pin_state_str;
+    guint32 remaining_attempts;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_command_done_get_result (response, &error)) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    if (!mbim_message_pin_response_parse (
+            response,
+            &pin_type,
+            &pin_state,
+            &remaining_attempts,
+            &error)) {
+        g_printerr ("error: couldn't parse response message: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    if (GPOINTER_TO_UINT (user_data))
+        g_print ("[%s] PIN operation successful\n\n",
+                 mbim_device_get_path_display (device));
+
+#undef VALIDATE_UNKNOWN
+#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
+
+    pin_state_str = mbim_pin_state_get_string (pin_state);
+
+    g_print ("[%s] Pin Info:\n"
+             "\t         Pin State: '%s'\n",
+             mbim_device_get_path_display (device),
+             VALIDATE_UNKNOWN (pin_state_str));
+    if (pin_type != MBIM_PIN_TYPE_UNKNOWN) {
+        const gchar *pin_type_str;
+
+        pin_type_str = mbim_pin_type_get_string (pin_type);
+        g_print ("\t           PinType: '%s'\n"
+                 "\tRemaining attempts: '%u'\n",
+                 VALIDATE_UNKNOWN (pin_type_str),
+                 remaining_attempts);
+    }
+
+    mbim_message_unref (response);
+    shutdown (TRUE);
+}
+
+static gboolean
+set_pin_input_parse (guint         n_expected,
+                     const gchar  *str,
+                     gchar       **pin,
+                     gchar       **new_pin)
+{
+    gchar **split;
+
+    g_assert (n_expected == 1 || n_expected == 2);
+    g_assert (pin != NULL);
+    g_assert (new_pin != NULL);
+
+    /* Format of the string is:
+     *    "[(current PIN)]"
+     * or:
+     *    "[(current PIN),(new PIN)]"
+     */
+    split = g_strsplit (str, ",", -1);
+
+    if (g_strv_length (split) > n_expected) {
+        g_printerr ("error: couldn't parse input string, too many arguments\n");
+        g_strfreev (split);
+        return FALSE;
+    }
+
+    if (g_strv_length (split) < n_expected) {
+        g_printerr ("error: couldn't parse input string, missing arguments\n");
+        g_strfreev (split);
+        return FALSE;
+    }
+
+    *pin = split[0];
+    *new_pin = split[1] ? split[1] : NULL;
+
+    g_free (split);
+    return TRUE;
+}
+
+enum {
+    CONNECTION_STATUS,
+    CONNECT,
+    DISCONNECT
+};
+
+static void
+connect_ready (MbimDevice   *device,
+               GAsyncResult *res,
+               gpointer user_data)
+{
+    MbimMessage *response;
+    GError *error = NULL;
+    guint32 session_id;
+    MbimActivationState activation_state;
+    MbimVoiceCallState voice_call_state;
+    MbimContextIpType ip_type;
+    const MbimUuid *context_type;
+    guint32 nw_error;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_command_done_get_result (response, &error)) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        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);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    switch (GPOINTER_TO_UINT (user_data)) {
+    case CONNECT:
+        g_print ("[%s] Successfully connected\n\n",
+                 mbim_device_get_path_display (device));
+        break;
+    case DISCONNECT:
+        g_print ("[%s] Successfully disconnected\n\n",
+                 mbim_device_get_path_display (device));
+        break;
+    default:
+        break;
+    }
+
+#undef VALIDATE_UNKNOWN
+#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
+
+    g_print ("[%s] Connection status:\n"
+             "\t      Session ID: '%u'\n"
+             "\tActivation state: '%s'\n"
+             "\tVoice call state: '%s'\n"
+             "\t         IP type: '%s'\n"
+             "\t    Context type: '%s'\n"
+             "\t   Network error: '%s'\n",
+             mbim_device_get_path_display (device),
+             session_id,
+             VALIDATE_UNKNOWN (mbim_activation_state_get_string (activation_state)),
+             VALIDATE_UNKNOWN (mbim_voice_call_state_get_string (voice_call_state)),
+             VALIDATE_UNKNOWN (mbim_context_ip_type_get_string (ip_type)),
+             VALIDATE_UNKNOWN (mbim_context_type_get_string (mbim_uuid_to_context_type (context_type))),
+             VALIDATE_UNKNOWN (mbim_nw_error_get_string (nw_error)));
+
+    mbim_message_unref (response);
+    shutdown (TRUE);
+}
+
+static gboolean
+set_connect_activate_parse (const gchar       *str,
+                            gchar            **apn,
+                            MbimAuthProtocol  *auth_protocol,
+                            gchar            **username,
+                            gchar            **password)
+{
+    gchar **split;
+
+    g_assert (apn != NULL);
+    g_assert (auth_protocol != NULL);
+    g_assert (username != NULL);
+    g_assert (password != NULL);
+
+    /* Format of the string is:
+     *    "[(APN),(PAP|CHAP|MSCHAPV2),(Username),(Password)]"
+     */
+    split = g_strsplit (str, ",", -1);
+
+    if (g_strv_length (split) > 4) {
+        g_printerr ("error: couldn't parse input string, too many arguments\n");
+        g_strfreev (split);
+        return FALSE;
+    }
+
+    if (g_strv_length (split) < 1) {
+        g_printerr ("error: couldn't parse input string, missing arguments\n");
+        g_strfreev (split);
+        return FALSE;
+    }
+
+    /* APN */
+    *apn = split[0];
+
+    /* Some defaults */
+    *auth_protocol = MBIM_AUTH_PROTOCOL_NONE;
+    *username = NULL;
+    *password = NULL;
+
+    /* Use authentication method */
+    if (split[1]) {
+        if (g_ascii_strcasecmp (split[1], "PAP") == 0)
+            *auth_protocol = MBIM_AUTH_PROTOCOL_PAP;
+        else if (g_ascii_strcasecmp (split[1], "CHAP") == 0)
+            *auth_protocol = MBIM_AUTH_PROTOCOL_CHAP;
+        else if (g_ascii_strcasecmp (split[1], "MSCHAPV2") == 0)
+            *auth_protocol = MBIM_AUTH_PROTOCOL_MSCHAPV2;
+        else
+            *auth_protocol = MBIM_AUTH_PROTOCOL_NONE;
+
+        /* Username */
+        if (split[2]) {
+            *username = split[2];
+
+            /* Password */
+            if (split[3])
+                *password = split[3];
+        }
+    }
+
+    g_free (split);
+    return TRUE;
+}
+
+static void
+home_provider_ready (MbimDevice   *device,
+                     GAsyncResult *res,
+                     gpointer user_data)
+{
+    MbimMessage *response;
+    GError *error = NULL;
+    MbimProvider *provider;
+    gchar *provider_state_str;
+    gchar *cellular_class_str;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_command_done_get_result (response, &error)) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    if (!mbim_message_home_provider_response_parse (response,
+                                                    &provider,
+                                                    &error)) {
+        g_printerr ("error: couldn't parse response message: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    provider_state_str = mbim_provider_state_build_string_from_mask (provider->provider_state);
+    cellular_class_str = mbim_cellular_class_build_string_from_mask (provider->cellular_class);
+
+#undef VALIDATE_UNKNOWN
+#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
+
+    g_print ("[%s] Home provider:\n"
+             "\t   Provider ID: '%s'\n"
+             "\t Provider Name: '%s'\n"
+             "\t         State: '%s'\n"
+             "\tCellular class: '%s'\n"
+             "\t          RSSI: '%u'\n"
+             "\t    Error rate: '%u'\n",
+             mbim_device_get_path_display (device),
+             VALIDATE_UNKNOWN (provider->provider_id),
+             VALIDATE_UNKNOWN (provider->provider_name),
+             VALIDATE_UNKNOWN (provider_state_str),
+             VALIDATE_UNKNOWN (cellular_class_str),
+             provider->rssi,
+             provider->error_rate);
+
+    g_free (cellular_class_str);
+    g_free (provider_state_str);
+
+    mbim_provider_free (provider);
+    mbim_message_unref (response);
+    shutdown (TRUE);
+}
+
+static void
+preferred_providers_ready (MbimDevice   *device,
+                           GAsyncResult *res,
+                           gpointer user_data)
+{
+    MbimMessage *response;
+    GError *error = NULL;
+    MbimProvider **providers;
+    guint n_providers;
+    guint i;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_command_done_get_result (response, &error)) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    if (!mbim_message_preferred_providers_response_parse (response,
+                                                          &n_providers,
+                                                          &providers,
+                                                          &error)) {
+        g_printerr ("error: couldn't parse response message: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    if (!n_providers)
+        g_print ("[%s] No preferred providers given\n",
+                 mbim_device_get_path_display (device));
+    else
+        g_print ("[%s] Preferred providers (%u):\n",
+                 mbim_device_get_path_display (device),
+                 n_providers);
+
+    for (i = 0; i < n_providers; i++) {
+        gchar *provider_state_str;
+        gchar *cellular_class_str;
+
+        provider_state_str = mbim_provider_state_build_string_from_mask (providers[i]->provider_state);
+        cellular_class_str = mbim_cellular_class_build_string_from_mask (providers[i]->cellular_class);
+
+#undef VALIDATE_UNKNOWN
+#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
+
+        g_print ("\tProvider [%u]:\n"
+                 "\t\t    Provider ID: '%s'\n"
+                 "\t\t  Provider Name: '%s'\n"
+                 "\t\t          State: '%s'\n"
+                 "\t\t Cellular class: '%s'\n"
+                 "\t\t           RSSI: '%u'\n"
+                 "\t\t     Error rate: '%u'\n",
+                 i,
+                 VALIDATE_UNKNOWN (providers[i]->provider_id),
+                 VALIDATE_UNKNOWN (providers[i]->provider_name),
+                 VALIDATE_UNKNOWN (provider_state_str),
+                 VALIDATE_UNKNOWN (cellular_class_str),
+                 providers[i]->rssi,
+                 providers[i]->error_rate);
+
+        g_free (cellular_class_str);
+        g_free (provider_state_str);
+    }
+
+    mbim_provider_array_free (providers);
+    mbim_message_unref (response);
+    shutdown (TRUE);
+}
+
+static void
+visible_providers_ready (MbimDevice   *device,
+                           GAsyncResult *res,
+                           gpointer user_data)
+{
+    MbimMessage *response;
+    GError *error = NULL;
+    MbimProvider **providers;
+    guint n_providers;
+    guint i;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_command_done_get_result (response, &error)) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    if (!mbim_message_visible_providers_response_parse (response,
+                                                        &n_providers,
+                                                        &providers,
+                                                        &error)) {
+        g_printerr ("error: couldn't parse response message: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    if (!n_providers)
+        g_print ("[%s] No visible providers given\n",
+                 mbim_device_get_path_display (device));
+    else
+        g_print ("[%s] Visible providers (%u):\n",
+                 mbim_device_get_path_display (device),
+                 n_providers);
+
+    for (i = 0; i < n_providers; i++) {
+        gchar *provider_state_str;
+        gchar *cellular_class_str;
+
+        provider_state_str = mbim_provider_state_build_string_from_mask (providers[i]->provider_state);
+        cellular_class_str = mbim_cellular_class_build_string_from_mask (providers[i]->cellular_class);
+
+#undef VALIDATE_UNKNOWN
+#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
+
+        g_print ("\tProvider [%u]:\n"
+                 "\t\t    Provider ID: '%s'\n"
+                 "\t\t  Provider Name: '%s'\n"
+                 "\t\t          State: '%s'\n"
+                 "\t\t Cellular class: '%s'\n"
+                 "\t\t           RSSI: '%u'\n"
+                 "\t\t     Error rate: '%u'\n",
+                 i,
+                 VALIDATE_UNKNOWN (providers[i]->provider_id),
+                 VALIDATE_UNKNOWN (providers[i]->provider_name),
+                 VALIDATE_UNKNOWN (provider_state_str),
+                 VALIDATE_UNKNOWN (cellular_class_str),
+                 providers[i]->rssi,
+                 providers[i]->error_rate);
+
+        g_free (cellular_class_str);
+        g_free (provider_state_str);
+    }
+
+    mbim_provider_array_free (providers);
+    mbim_message_unref (response);
+    shutdown (TRUE);
+}
+
+static void
+register_state_ready (MbimDevice   *device,
+                      GAsyncResult *res,
+                      gpointer user_data)
+{
+    MbimMessage *response;
+    GError *error = NULL;
+    MbimNwError nw_error;
+    MbimRegisterState register_state;
+    MbimRegisterMode register_mode;
+    MbimDataClass available_data_classes;
+    gchar *available_data_classes_str;
+    MbimCellularClass cellular_class;
+    gchar *cellular_class_str;
+    gchar *provider_id;
+    gchar *provider_name;
+    gchar *roaming_text;
+    MbimRegistrationFlag registration_flag;
+    gchar *registration_flag_str;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_command_done_get_result (response, &error)) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        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);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    if (GPOINTER_TO_UINT (user_data))
+        g_print ("[%s] Successfully launched automatic registration\n\n",
+                 mbim_device_get_path_display (device));
+
+    available_data_classes_str = mbim_data_class_build_string_from_mask (available_data_classes);
+    cellular_class_str = mbim_cellular_class_build_string_from_mask (cellular_class);
+    registration_flag_str = mbim_registration_flag_build_string_from_mask (registration_flag);
+
+#undef VALIDATE_UNKNOWN
+#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
+
+    g_print ("[%s] Registration status:\n"
+             "\t         Network error: '%s'\n"
+             "\t        Register state: '%s'\n"
+             "\t         Register mode: '%s'\n"
+             "\tAvailable data classes: '%s'\n"
+             "\tCurrent cellular class: '%s'\n"
+             "\t           Provider ID: '%s'\n"
+             "\t         Provider name: '%s'\n"
+             "\t          Roaming text: '%s'\n"
+             "\t    Registration flags: '%s'\n",
+             mbim_device_get_path_display (device),
+             VALIDATE_UNKNOWN (mbim_nw_error_get_string (nw_error)),
+             VALIDATE_UNKNOWN (mbim_register_state_get_string (register_state)),
+             VALIDATE_UNKNOWN (mbim_register_mode_get_string (register_mode)),
+             VALIDATE_UNKNOWN (available_data_classes_str),
+             VALIDATE_UNKNOWN (cellular_class_str),
+             VALIDATE_UNKNOWN (provider_id),
+             VALIDATE_UNKNOWN (provider_name),
+             VALIDATE_UNKNOWN (roaming_text),
+             VALIDATE_UNKNOWN (registration_flag_str));
+
+    g_free (available_data_classes_str);
+    g_free (cellular_class_str);
+    g_free (registration_flag_str);
+    g_free (provider_name);
+    g_free (provider_id);
+    g_free (roaming_text);
+
+    mbim_message_unref (response);
+    shutdown (TRUE);
+}
+
+static void
+signal_state_ready (MbimDevice   *device,
+                    GAsyncResult *res,
+                    gpointer user_data)
+{
+    MbimMessage *response;
+    GError *error = NULL;
+    guint32 rssi;
+    guint32 error_rate;
+    guint32 signal_strength_interval;
+    guint32 rssi_threshold;
+    guint32 error_rate_threshold;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_command_done_get_result (response, &error)) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        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);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    g_print ("[%s] Signal state:\n"
+             "\t          RSSI [0-31,99]: '%u'\n"
+             "\t     Error rate [0-7,99]: '%u'\n"
+             "\tSignal strength interval: '%u'\n"
+             "\t          RSSI threshold: '%u'\n",
+             mbim_device_get_path_display (device),
+             rssi,
+             error_rate,
+             signal_strength_interval,
+             rssi_threshold);
+
+    if (error_rate_threshold == 0xFFFFFFFF)
+        g_print ("\t    Error rate threshold: 'unspecified'\n");
+    else
+        g_print ("\t    Error rate threshold: '%u'\n", error_rate_threshold);
+
+    mbim_message_unref (response);
+    shutdown (TRUE);
+}
+
+enum {
+    PACKET_SERVICE_STATUS,
+    PACKET_SERVICE_ATTACH,
+    PACKET_SERVICE_DETACH
+};
+
+static void
+packet_service_ready (MbimDevice   *device,
+                      GAsyncResult *res,
+                      gpointer user_data)
+{
+    MbimMessage *response;
+    GError *error = NULL;
+    guint32 nw_error;
+    MbimPacketServiceState packet_service_state;
+    MbimDataClass highest_available_data_class;
+    gchar *highest_available_data_class_str;
+    guint64 uplink_speed;
+    guint64 downlink_speed;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_command_done_get_result (response, &error)) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        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);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    switch (GPOINTER_TO_UINT (user_data)) {
+    case PACKET_SERVICE_ATTACH:
+        g_print ("[%s] Successfully attached to packet service\n\n",
+                 mbim_device_get_path_display (device));
+        break;
+    case PACKET_SERVICE_DETACH:
+        g_print ("[%s] Successfully detached from packet service\n\n",
+                 mbim_device_get_path_display (device));
+        break;
+    default:
+        break;
+    }
+
+#undef VALIDATE_UNKNOWN
+#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
+
+    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"
+             "\t  Packet service state: '%s'\n"
+             "\tAvailable data classes: '%s'\n"
+             "\t          Uplink speed: '%" G_GUINT64_FORMAT " bps'\n"
+             "\t        Downlink speed: '%" G_GUINT64_FORMAT " bps'\n",
+             mbim_device_get_path_display (device),
+             VALIDATE_UNKNOWN (mbim_nw_error_get_string (nw_error)),
+             VALIDATE_UNKNOWN (mbim_packet_service_state_get_string (packet_service_state)),
+             VALIDATE_UNKNOWN (highest_available_data_class_str),
+             uplink_speed,
+             downlink_speed);
+
+    g_free (highest_available_data_class_str);
+
+    mbim_message_unref (response);
+    shutdown (TRUE);
+}
+
+static void
+packet_statistics_ready (MbimDevice   *device,
+                         GAsyncResult *res)
+{
+    MbimMessage *response;
+    GError *error = NULL;
+    guint32 in_discards;
+    guint32 in_errors;
+    guint64 in_octets;
+    guint64 in_packets;
+    guint64 out_octets;
+    guint64 out_packets;
+    guint32 out_errors;
+    guint32 out_discards;
+
+    response = mbim_device_command_finish (device, res, &error);
+    if (!response || !mbim_message_command_done_get_result (response, &error)) {
+        g_printerr ("error: operation failed: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    if (!mbim_message_packet_statistics_response_parse (response,
+                                                        &in_discards,
+                                                        &in_errors,
+                                                        &in_octets,
+                                                        &in_packets,
+                                                        &out_octets,
+                                                        &out_packets,
+                                                        &out_errors,
+                                                        &out_discards,
+                                                        &error)) {
+        g_printerr ("error: couldn't parse response message: %s\n", error->message);
+        g_error_free (error);
+        shutdown (FALSE);
+        return;
+    }
+
+    g_print ("[%s] Packet statistics:\n"
+             "\t   Octets (in): '%" G_GUINT64_FORMAT "'\n"
+             "\t  Packets (in): '%" G_GUINT64_FORMAT "'\n"
+             "\t Discards (in): '%u'\n"
+             "\t   Errors (in): '%u'\n"
+             "\t  Octets (out): '%" G_GUINT64_FORMAT "'\n"
+             "\t Packets (out): '%" G_GUINT64_FORMAT "'\n"
+             "\tDiscards (out): '%u'\n"
+             "\t  Errors (out): '%u'\n",
+             mbim_device_get_path_display (device),
+             in_octets,
+             in_packets,
+             in_discards,
+             in_errors,
+             out_octets,
+             out_packets,
+             out_discards,
+             out_errors);
+
+    mbim_message_unref (response);
+    shutdown (TRUE);
+}
+
+void
+mbimcli_basic_connect_run (MbimDevice   *device,
+                           GCancellable *cancellable)
+{
+    /* Initialize context */
+    ctx = g_slice_new (Context);
+    ctx->device = g_object_ref (device);
+    if (cancellable)
+        ctx->cancellable = g_object_ref (cancellable);
+
+    /* Request to get capabilities? */
+    if (query_device_caps_flag) {
+        MbimMessage *request;
+
+        g_debug ("Asynchronously querying device capabilities...");
+        request = (mbim_message_device_caps_query_new (NULL));
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)query_device_caps_ready,
+                             NULL);
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Request to get subscriber ready status? */
+    if (query_subscriber_ready_status_flag) {
+        MbimMessage *request;
+
+        g_debug ("Asynchronously querying subscriber ready status...");
+        request = (mbim_message_subscriber_ready_status_query_new (NULL));
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)query_subscriber_ready_status_ready,
+                             NULL);
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Request to get radio state? */
+    if (query_radio_state_flag) {
+        MbimMessage *request;
+
+        g_debug ("Asynchronously querying radio state...");
+        request = (mbim_message_radio_state_query_new (NULL));
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)query_radio_state_ready,
+                             NULL);
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Request to query device services? */
+    if (query_device_services_flag) {
+        MbimMessage *request;
+
+        g_debug ("Asynchronously querying device services...");
+        request = (mbim_message_device_services_query_new (NULL));
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)query_device_services_ready,
+                             NULL);
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Query PIN state? */
+    if (query_pin_flag) {
+        MbimMessage *request;
+
+        g_debug ("Asynchronously querying PIN state...");
+        request = (mbim_message_pin_query_new (NULL));
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)pin_ready,
+                             GUINT_TO_POINTER (FALSE));
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Set PIN? */
+    if (set_pin_enter_str ||
+        set_pin_change_str ||
+        set_pin_enable_str ||
+        set_pin_disable_str ||
+        set_pin_enter_puk_str) {
+        MbimMessage *request;
+        guint n_expected;
+        MbimPinType pin_type;
+        MbimPinOperation pin_operation;
+        gchar *pin;
+        gchar *new_pin;
+        const gchar *input = NULL;
+        GError *error = NULL;
+
+        if (set_pin_enter_puk_str) {
+            g_debug ("Asynchronously entering PUK...");
+            pin_type = MBIM_PIN_TYPE_PUK1;
+            input = set_pin_enter_puk_str;
+            n_expected = 2;
+            pin_operation = MBIM_PIN_OPERATION_ENTER;
+        } else {
+            pin_type = MBIM_PIN_TYPE_PIN1;
+            if (set_pin_change_str) {
+                g_debug ("Asynchronously changing PIN...");
+                input = set_pin_change_str;
+                n_expected = 2;
+                pin_operation = MBIM_PIN_OPERATION_CHANGE;
+            } else if (set_pin_enable_str) {
+                g_debug ("Asynchronously enabling PIN...");
+                input = set_pin_enable_str;
+                n_expected = 1;
+                pin_operation = MBIM_PIN_OPERATION_ENABLE;
+            } else if (set_pin_disable_str) {
+                g_debug ("Asynchronously disabling PIN...");
+                input = set_pin_disable_str;
+                n_expected = 1;
+                pin_operation = MBIM_PIN_OPERATION_DISABLE;
+            } else if (set_pin_enter_str) {
+                g_debug ("Asynchronously entering PIN...");
+                input = set_pin_enter_str;
+                n_expected = 1;
+                pin_operation = MBIM_PIN_OPERATION_ENTER;
+            } else
+                g_assert_not_reached ();
+        }
+
+        if (!set_pin_input_parse (n_expected, input, &pin, &new_pin)) {
+            shutdown (FALSE);
+            return;
+        }
+
+        request = (mbim_message_pin_set_new (pin_type,
+                                             pin_operation,
+                                             pin,
+                                             new_pin,
+                                             &error));
+        g_free (pin);
+        g_free (new_pin);
+
+        if (!request) {
+            g_printerr ("error: couldn't create request: %s\n", error->message);
+            g_error_free (error);
+            shutdown (FALSE);
+            return;
+        }
+
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)pin_ready,
+                             GUINT_TO_POINTER (TRUE));
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Query home provider? */
+    if (query_home_provider_flag) {
+        MbimMessage *request;
+
+        request = mbim_message_home_provider_query_new (NULL);
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)home_provider_ready,
+                             NULL);
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Query preferred providers? */
+    if (query_preferred_providers_flag) {
+        MbimMessage *request;
+
+        request = mbim_message_preferred_providers_query_new (NULL);
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)preferred_providers_ready,
+                             NULL);
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Query visible providers? */
+    if (query_visible_providers_flag) {
+        MbimMessage *request;
+
+        request = mbim_message_visible_providers_query_new (MBIM_VISIBLE_PROVIDERS_ACTION_FULL_SCAN, NULL);
+        mbim_device_command (ctx->device,
+                             request,
+                             60, /* longer timeout, needs to scan */
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)visible_providers_ready,
+                             NULL);
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Query registration status? */
+    if (query_register_state_flag) {
+        MbimMessage *request;
+
+        request = mbim_message_register_state_query_new (NULL);
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)register_state_ready,
+                             GUINT_TO_POINTER (FALSE));
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Launch automatic registration? */
+    if (set_register_state_automatic_flag) {
+        MbimMessage *request;
+        GError *error = NULL;
+
+        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);
+            g_error_free (error);
+            shutdown (FALSE);
+            return;
+        }
+
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)register_state_ready,
+                             GUINT_TO_POINTER (TRUE));
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Query signal status? */
+    if (query_signal_state_flag) {
+        MbimMessage *request;
+
+        request = mbim_message_signal_state_query_new (NULL);
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)signal_state_ready,
+                             NULL);
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Query packet service status? */
+    if (query_packet_service_flag) {
+        MbimMessage *request;
+
+        request = mbim_message_packet_service_query_new (NULL);
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)packet_service_ready,
+                             GUINT_TO_POINTER (PACKET_SERVICE_STATUS));
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Launch packet attach or detach? */
+    if (set_packet_service_attach_flag ||
+        set_packet_service_detach_flag) {
+        MbimMessage *request;
+        MbimPacketServiceAction action;
+        GError *error = NULL;
+
+        if (set_packet_service_attach_flag)
+            action = MBIM_PACKET_SERVICE_ACTION_ATTACH;
+        else if (set_packet_service_detach_flag)
+            action = MBIM_PACKET_SERVICE_ACTION_DETACH;
+        else
+            g_assert_not_reached ();
+
+        request = mbim_message_packet_service_set_new (action, &error);
+        if (!request) {
+            g_printerr ("error: couldn't create request: %s\n", error->message);
+            g_error_free (error);
+            shutdown (FALSE);
+            return;
+        }
+
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)packet_service_ready,
+                             GUINT_TO_POINTER (set_packet_service_attach_flag ?
+                                               PACKET_SERVICE_ATTACH :
+                                               PACKET_SERVICE_DETACH));
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Query connection status? */
+    if (query_connect_flag) {
+        MbimMessage *request;
+        GError *error = NULL;
+
+        request = mbim_message_connect_query_new (0,
+                                                  MBIM_ACTIVATION_COMMAND_ACTIVATE,
+                                                  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);
+            g_error_free (error);
+            shutdown (FALSE);
+            return;
+        }
+
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)connect_ready,
+                             GUINT_TO_POINTER (CONNECTION_STATUS));
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Connect? */
+    if (set_connect_activate_str) {
+        MbimMessage *request;
+        GError *error = NULL;
+        gchar *apn;
+        MbimAuthProtocol auth_protocol;
+        gchar *username = NULL;
+        gchar *password = NULL;
+
+        if (!set_connect_activate_parse (set_connect_activate_str,
+                                         &apn,
+                                         &auth_protocol,
+                                         &username,
+                                         &password)) {
+            shutdown (FALSE);
+            return;
+        }
+
+        request = mbim_message_connect_set_new (0,
+                                                MBIM_ACTIVATION_COMMAND_ACTIVATE,
+                                                apn,
+                                                username,
+                                                password,
+                                                MBIM_COMPRESSION_NONE,
+                                                auth_protocol,
+                                                MBIM_CONTEXT_IP_TYPE_DEFAULT,
+                                                mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
+                                                &error);
+        g_free (apn);
+        g_free (username);
+        g_free (password);
+
+        if (!request) {
+            g_printerr ("error: couldn't create request: %s\n", error->message);
+            g_error_free (error);
+            shutdown (FALSE);
+            return;
+        }
+
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)connect_ready,
+                             GUINT_TO_POINTER (CONNECT));
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Disconnect? */
+    if (set_connect_deactivate_flag) {
+        MbimMessage *request;
+        GError *error = NULL;
+
+        request = mbim_message_connect_set_new (0,
+                                                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);
+            g_error_free (error);
+            shutdown (FALSE);
+            return;
+        }
+
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)connect_ready,
+                             GUINT_TO_POINTER (DISCONNECT));
+        mbim_message_unref (request);
+        return;
+    }
+
+    /* Packet statistics? */
+    if (query_packet_statistics_flag) {
+        MbimMessage *request;
+
+        request = mbim_message_packet_statistics_query_new (NULL);
+        mbim_device_command (ctx->device,
+                             request,
+                             10,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)packet_statistics_ready,
+                             NULL);
+        mbim_message_unref (request);
+        return;
+    }
+
+    g_warn_if_reached ();
+}
diff --git a/cli/mbimcli.c b/cli/mbimcli.c
new file mode 100644
index 0000000..e4f9215
--- /dev/null
+++ b/cli/mbimcli.c
@@ -0,0 +1,418 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * mbimcli -- Command line interface to control MBIM devices
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <gio/gio.h>
+
+#include <libmbim-glib.h>
+
+#include "mbimcli.h"
+
+#define PROGRAM_NAME    "mbimcli"
+#define PROGRAM_VERSION PACKAGE_VERSION
+
+/* Globals */
+static GMainLoop *loop;
+static GCancellable *cancellable;
+static MbimDevice *device;
+static MbimService service;
+static gboolean operation_status;
+
+/* Main options */
+static gchar *device_str;
+static gchar *no_open_str;
+static gboolean no_close_flag;
+static gboolean noop_flag;
+static gboolean verbose_flag;
+static gboolean silent_flag;
+static gboolean version_flag;
+
+static GOptionEntry main_entries[] = {
+    { "device", 'd', 0, G_OPTION_ARG_STRING, &device_str,
+      "Specify device path",
+      "[PATH]"
+    },
+    { "no-open", 0, 0, G_OPTION_ARG_STRING, &no_open_str,
+      "Do not explicitly open the MBIM device before running the command",
+      "[Transaction ID]"
+    },
+    { "no-close", 0, 0, G_OPTION_ARG_NONE, &no_close_flag,
+      "Do not close the MBIM device after running the command",
+      NULL
+    },
+    { "noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag,
+      "Don't run any command",
+      NULL
+    },
+    { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag,
+      "Run action with verbose logs, including the debug ones",
+      NULL
+    },
+    { "silent", 0, 0, G_OPTION_ARG_NONE, &silent_flag,
+      "Run action with no logs; not even the error/warning ones",
+      NULL
+    },
+    { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag,
+      "Print version",
+      NULL
+    },
+    { NULL }
+};
+
+static void
+signals_handler (int signum)
+{
+    if (cancellable) {
+        /* Ignore consecutive requests of cancellation */
+        if (!g_cancellable_is_cancelled (cancellable)) {
+            g_printerr ("%s\n",
+                        "cancelling the operation...\n");
+            g_cancellable_cancel (cancellable);
+        }
+        return;
+    }
+
+    if (loop &&
+        g_main_loop_is_running (loop)) {
+        g_printerr ("%s\n",
+                    "cancelling the main loop...\n");
+        g_main_loop_quit (loop);
+    }
+}
+
+static void
+log_handler (const gchar *log_domain,
+             GLogLevelFlags log_level,
+             const gchar *message,
+             gpointer user_data)
+{
+    const gchar *log_level_str;
+    time_t now;
+    gchar time_str[64];
+    struct tm *local_time;
+    gboolean err;
+
+    /* Nothing to do if we're silent */
+    if (silent_flag)
+        return;
+
+    now = time ((time_t *) NULL);
+    local_time = localtime (&now);
+    strftime (time_str, 64, "%d %b %Y, %H:%M:%S", local_time);
+    err = FALSE;
+
+    switch (log_level) {
+    case G_LOG_LEVEL_WARNING:
+        log_level_str = "-Warning **";
+        err = TRUE;
+        break;
+
+    case G_LOG_LEVEL_CRITICAL:
+    case G_LOG_FLAG_FATAL:
+    case G_LOG_LEVEL_ERROR:
+        log_level_str = "-Error **";
+        err = TRUE;
+        break;
+
+    case G_LOG_LEVEL_DEBUG:
+        log_level_str = "[Debug]";
+        break;
+
+    default:
+        log_level_str = "";
+        break;
+    }
+
+    if (!verbose_flag && !err)
+        return;
+
+    g_fprintf (err ? stderr : stdout,
+               "[%s] %s %s\n",
+               time_str,
+               log_level_str,
+               message);
+}
+
+static void
+print_version_and_exit (void)
+{
+    g_print ("\n"
+             PROGRAM_NAME " " PROGRAM_VERSION "\n"
+             "Copyright (2013) Aleksander Morgado\n"
+             "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl-2.0.html>\n"
+             "This is free software: you are free to change and redistribute it.\n"
+             "There is NO WARRANTY, to the extent permitted by law.\n"
+             "\n");
+    exit (EXIT_SUCCESS);
+}
+
+/*****************************************************************************/
+/* Running asynchronously */
+
+static void
+device_close_ready (MbimDevice   *dev,
+                    GAsyncResult *res)
+{
+    GError *error = NULL;
+
+    if (!mbim_device_close_finish (dev, res, &error)) {
+        g_printerr ("error: couldn't close device: %s", error->message);
+        g_error_free (error);
+    } else
+        g_debug ("Device closed");
+
+    /* If we left the device open, dump next transaction id */
+    if (no_close_flag) {
+        guint transaction_id;
+
+        g_object_get (dev,
+                      MBIM_DEVICE_TRANSACTION_ID, &transaction_id,
+                      NULL);
+
+        g_print ("[%s] Session not closed:\n"
+                 "\t    TRID: '%u'\n",
+                 mbim_device_get_path_display (dev),
+                 transaction_id);
+    }
+
+    g_main_loop_quit (loop);
+}
+
+void
+mbimcli_async_operation_done (gboolean reported_operation_status)
+{
+    /* Keep the result of the operation */
+    operation_status = reported_operation_status;
+
+    if (cancellable) {
+        g_object_unref (cancellable);
+        cancellable = NULL;
+    }
+
+    /* Set the in-session setup */
+    g_object_set (device,
+                  MBIM_DEVICE_IN_SESSION, no_close_flag,
+                  NULL);
+
+    /* Close the device */
+    mbim_device_close (device,
+                       15,
+                       cancellable,
+                       (GAsyncReadyCallback) device_close_ready,
+                       NULL);
+}
+
+static void
+device_open_ready (MbimDevice   *dev,
+                   GAsyncResult *res)
+{
+    GError *error = NULL;
+
+    if (!mbim_device_open_finish (dev, res, &error)) {
+        g_printerr ("error: couldn't open the MbimDevice: %s\n",
+                    error->message);
+        exit (EXIT_FAILURE);
+    }
+
+    g_debug ("MBIM Device at '%s' ready",
+             mbim_device_get_path_display (dev));
+
+    /* If no operation requested, finish */
+    if (noop_flag) {
+        mbimcli_async_operation_done (TRUE);
+        return;
+    }
+
+    /* Run the service-specific action */
+    switch (service) {
+    case MBIM_SERVICE_BASIC_CONNECT:
+        mbimcli_basic_connect_run (dev, cancellable);
+        return;
+    default:
+        g_assert_not_reached ();
+    }
+}
+
+static guint
+read_transaction_id (const gchar *str)
+{
+    gulong num;
+
+    if (!str || !str[0])
+        return 0;
+
+    for (num = 0; str[num]; num++) {
+        if (!g_ascii_isdigit (str[num]))
+            return 0;
+    }
+
+    errno = 0;
+    num = strtoul (str, NULL, 10);
+    if (!errno && num <= G_MAXUINT) {
+        return (guint)num;
+    }
+    return 0;
+}
+
+static void
+device_new_ready (GObject      *unused,
+                  GAsyncResult *res)
+{
+    GError *error = NULL;
+
+    device = mbim_device_new_finish (res, &error);
+    if (!device) {
+        g_printerr ("error: couldn't create MbimDevice: %s\n",
+                    error->message);
+        exit (EXIT_FAILURE);
+    }
+
+    /* Set the in-session setup */
+    if (no_open_str) {
+        guint transaction_id;
+
+        transaction_id = read_transaction_id (no_open_str);
+        if (!transaction_id) {
+            g_printerr ("error: invalid transaction ID specified: %s\n",
+                        no_open_str);
+            exit (EXIT_FAILURE);
+        }
+
+        g_object_set (device,
+                      MBIM_DEVICE_IN_SESSION,     TRUE,
+                      MBIM_DEVICE_TRANSACTION_ID, transaction_id,
+                      NULL);
+    }
+
+    /* Open the device */
+    mbim_device_open (device,
+                      15,
+                      cancellable,
+                      (GAsyncReadyCallback) device_open_ready,
+                      NULL);
+}
+
+/*****************************************************************************/
+
+static void
+parse_actions (void)
+{
+    guint actions_enabled = 0;
+
+    /* Basic Connect options? */
+    if (mbimcli_basic_connect_options_enabled ()) {
+        service = MBIM_SERVICE_BASIC_CONNECT;
+        actions_enabled++;
+    }
+
+    /* Noop */
+    if (noop_flag)
+        actions_enabled++;
+
+    /* Cannot mix actions from different services */
+    if (actions_enabled > 1) {
+        g_printerr ("error: cannot execute multiple actions of different services\n");
+        exit (EXIT_FAILURE);
+    }
+
+    /* No options? */
+    if (actions_enabled == 0) {
+        g_printerr ("error: no actions specified\n");
+        exit (EXIT_FAILURE);
+    }
+
+    /* Go on! */
+}
+
+int main (int argc, char **argv)
+{
+    GError *error = NULL;
+    GFile *file;
+    GOptionContext *context;
+
+    setlocale (LC_ALL, "");
+
+    g_type_init ();
+
+    /* Setup option context, process it and destroy it */
+    context = g_option_context_new ("- Control MBIM devices");
+	g_option_context_add_group (context,
+	                            mbimcli_basic_connect_get_option_group ());
+    g_option_context_add_main_entries (context, main_entries, NULL);
+    if (!g_option_context_parse (context, &argc, &argv, &error)) {
+        g_printerr ("error: %s\n",
+                    error->message);
+        exit (EXIT_FAILURE);
+    }
+	g_option_context_free (context);
+
+    if (version_flag)
+        print_version_and_exit ();
+
+    g_log_set_handler (NULL, G_LOG_LEVEL_MASK, log_handler, NULL);
+    g_log_set_handler ("Mbim", G_LOG_LEVEL_MASK, log_handler, NULL);
+    if (verbose_flag)
+        mbim_utils_set_traces_enabled (TRUE);
+
+    /* No device path given? */
+    if (!device_str) {
+        g_printerr ("error: no device path specified\n");
+        exit (EXIT_FAILURE);
+    }
+
+    /* Build new GFile from the commandline arg */
+    file = g_file_new_for_commandline_arg (device_str);
+
+    /* Setup signals */
+    signal (SIGINT, signals_handler);
+    signal (SIGHUP, signals_handler);
+    signal (SIGTERM, signals_handler);
+
+    /* Create requirements for async options */
+    cancellable = g_cancellable_new ();
+    loop = g_main_loop_new (NULL, FALSE);
+
+    parse_actions ();
+
+    /* Launch MbimDevice creation */
+    mbim_device_new (file,
+                     cancellable,
+                     (GAsyncReadyCallback)device_new_ready,
+                     NULL);
+    g_main_loop_run (loop);
+
+    if (cancellable)
+        g_object_unref (cancellable);
+    if (device)
+        g_object_unref (device);
+    g_main_loop_unref (loop);
+    g_object_unref (file);
+
+    return (operation_status ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/cli/mbimcli.h b/cli/mbimcli.h
new file mode 100644
index 0000000..78942df
--- /dev/null
+++ b/cli/mbimcli.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * mbimcli -- Command line interface to control MBIM devices
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include <glib.h>
+
+#ifndef __MBIMCLI_H__
+#define __MBIMCLI_H__
+
+/* Common */
+void mbimcli_async_operation_done (gboolean operation_status);
+
+/* Basic Connect group */
+GOptionGroup *mbimcli_basic_connect_get_option_group (void);
+gboolean      mbimcli_basic_connect_options_enabled  (void);
+void          mbimcli_basic_connect_run              (MbimDevice *device,
+                                                      GCancellable *cancellable);
+
+
+#endif /* __MBIMCLI_H__ */
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..5b80910
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,117 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_PREREQ([2.68])
+
+dnl The libmbim version number
+m4_define([mbim_major_version], [1])
+m4_define([mbim_minor_version], [5])
+m4_define([mbim_micro_version], [0])
+m4_define([mbim_version],
+          [mbim_major_version.mbim_minor_version.mbim_micro_version])
+
+dnl libtool versioning for libmbim-glib (-version-info c:r:a)
+dnl    If the interface is unchanged, but the implementation has changed or
+dnl        been fixed, then increment r.
+dnl    Otherwise, increment c and zero r.
+dnl        If the interface has grown (that is, the new library is compatible
+dnl            with old code), increment a.
+dnl        If the interface has changed in an incompatible way (that is,
+dnl            functions have changed or been removed), then zero a.
+m4_define([mbim_glib_lt_current],  [2])
+m4_define([mbim_glib_lt_revision], [0])
+m4_define([mbim_glib_lt_age],      [2])
+
+
+AC_INIT([libmbim], [mbim_version], [libmbim-devel@lists.freedesktop.org])
+AC_CONFIG_SRCDIR([config.h.in])
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_MACRO_DIR([m4])
+
+AM_INIT_AUTOMAKE([1.11 no-define no-dist-gzip dist-xz tar-ustar -Wno-portability])
+AM_MAINTAINER_MODE([enable])
+
+dnl Support silent build rules. Disable
+dnl by either passing --disable-silent-rules to configure or passing V=1
+dnl to make
+AM_SILENT_RULES([yes])
+
+dnl Required programs
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_INSTALL
+
+dnl Initialize libtool
+LT_PREREQ([2.2])
+LT_INIT
+
+dnl Specific warnings to always use
+LIBMBIM_COMPILER_WARNINGS
+
+dnl Version stuff
+MBIM_MAJOR_VERSION=mbim_major_version
+MBIM_MINOR_VERSION=mbim_minor_version
+MBIM_MICRO_VERSION=mbim_micro_version
+MBIM_VERSION=mbim_version
+AC_SUBST(MBIM_MAJOR_VERSION)
+AC_SUBST(MBIM_MINOR_VERSION)
+AC_SUBST(MBIM_MICRO_VERSION)
+AC_SUBST(MBIM_VERSION)
+
+dnl libtool version stuff
+MBIM_GLIB_LT_CURRENT=mbim_glib_lt_current
+MBIM_GLIB_LT_REVISION=mbim_glib_lt_revision
+MBIM_GLIB_LT_AGE=mbim_glib_lt_age
+AC_SUBST(MBIM_GLIB_LT_CURRENT)
+AC_SUBST(MBIM_GLIB_LT_REVISION)
+AC_SUBST(MBIM_GLIB_LT_AGE)
+
+dnl General dependencies for libmbim-glib
+PKG_CHECK_MODULES(LIBMBIM_GLIB,
+                  glib-2.0 >= 2.32
+                  gobject-2.0
+                  gio-2.0
+                  gudev-1.0 >= 147)
+AC_SUBST(LIBMBIM_GLIB_CFLAGS)
+AC_SUBST(LIBMBIM_GLIB_LIBS)
+
+dnl General dependencies for mbimcli
+PKG_CHECK_MODULES(MBIMCLI,
+                  glib-2.0 >= 2.32
+                  gobject-2.0
+                  gio-2.0)
+AC_SUBST(MBIMCLI_CFLAGS)
+AC_SUBST(MBIMCLI_LIBS)
+
+GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0`
+AC_SUBST(GLIB_MKENUMS)
+
+dnl Documentation
+GTK_DOC_CHECK(1.0)
+
+AC_CONFIG_FILES([Makefile
+                 build-aux/Makefile
+                 build-aux/templates/Makefile
+                 build-aux/mbim-codegen/Makefile
+                 data/Makefile
+                 data/pkg-config/Makefile
+                 data/pkg-config/mbim-glib.pc
+                 libmbim-glib/Makefile
+                 libmbim-glib/mbim-version.h
+                 libmbim-glib/generated/Makefile
+                 libmbim-glib/test/Makefile
+                 cli/Makefile
+                 utils/Makefile
+                 docs/Makefile
+                 docs/reference/Makefile
+                 docs/reference/libmbim-glib/Makefile
+                 docs/reference/libmbim-glib/version.xml])
+AC_OUTPUT
+
+echo "
+    libmbim $VERSION
+    ==============================================
+
+    compiler:                ${CC}
+    cflags:                  ${CFLAGS}
+    Maintainer mode:         ${USE_MAINTAINER_MODE}
+    Documentation:           ${enable_gtk_doc}
+"
diff --git a/data/Makefile.am b/data/Makefile.am
new file mode 100644
index 0000000..091c2e2
--- /dev/null
+++ b/data/Makefile.am
@@ -0,0 +1,10 @@
+SUBDIRS = . pkg-config
+
+EXTRA_DIST = \
+	mbim-service-basic-connect.json \
+	mbim-service-sms.json \
+	mbim-service-ussd.json \
+	mbim-service-auth.json \
+	mbim-service-phonebook.json \
+	mbim-service-stk.json \
+	mbim-service-dss.json
diff --git a/data/mbim-service-auth.json b/data/mbim-service-auth.json
new file mode 100644
index 0000000..b518e38
--- /dev/null
+++ b/data/mbim-service-auth.json
@@ -0,0 +1,89 @@
+
+[
+    // *********************************************************************************
+    { "type" : "Service",
+      "name" : "Auth" },
+
+  // *********************************************************************************
+  { "name"     : "Aka",
+    "service"  : "Auth",
+    "type"     : "Command",
+    "query"    : [ { "name"       : "Rand",
+                     "format"     : "byte-array",
+                     "array-size" : "16" },
+                   { "name"       : "Autn",
+                     "format"     : "byte-array",
+                     "array-size" : "16" } ],
+    "response" : [ { "name"       : "Res",
+                     "format"     : "byte-array",
+                     "array-size" : "16" },
+                   { "name"       : "ResLen",
+                     "format"     : "guint32" },
+                   { "name"       : "IntegratingKey",
+                     "format"     : "byte-array",
+                     "array-size" : "16" },
+                   { "name"       : "CipheringKey",
+                     "format"     : "byte-array",
+                     "array-size" : "16" },
+                   { "name"       : "Auts",
+                     "format"     : "byte-array",
+                     "array-size" : "14" } ] },
+
+  // *********************************************************************************
+  { "name"     : "Akap",
+    "service"  : "Auth",
+    "type"     : "Command",
+    "query"    : [ { "name"       : "Rand",
+                     "format"     : "byte-array",
+                     "array-size" : "16" },
+                   { "name"       : "Autn",
+                     "format"     : "byte-array",
+                     "array-size" : "16" },
+                   { "name"   : "NetworkName",
+                     "format" : "string" } ],
+    "response" : [ { "name"       : "Res",
+                     "format"     : "byte-array",
+                     "array-size" : "16" },
+                   { "name"       : "ResLen",
+                     "format"     : "guint32" },
+                   { "name"       : "IntegratingKey",
+                     "format"     : "byte-array",
+                     "array-size" : "16" },
+                   { "name"       : "CipheringKey",
+                     "format"     : "byte-array",
+                     "array-size" : "16" },
+                   { "name"       : "Auts",
+                     "format"     : "byte-array",
+                     "array-size" : "14" } ] },
+
+  // *********************************************************************************
+  { "name"     : "Sim",
+    "service"  : "Auth",
+    "type"     : "Command",
+    "query"    : [ { "name"       : "Rand1",
+                     "format"     : "byte-array",
+                     "array-size" : "16" },
+                   { "name"       : "Rand2",
+                     "format"     : "byte-array",
+                     "array-size" : "16" },
+                   { "name"       : "Rand3",
+                     "format"     : "byte-array",
+                     "array-size" : "16" },
+                   { "name"       : "N",
+                     "format"     : "guint32" } ],
+    "response" : [ { "name"       : "Sres1",
+                     "format"     : "guint32" },
+                   { "name"       : "Kc1",
+                     "format"     : "guint64" },
+                   { "name"       : "Sres2",
+                     "format"     : "guint32" },
+                   { "name"       : "Kc2",
+                     "format"     : "guint64" },
+                   { "name"       : "Sres3",
+                     "format"     : "guint32" },
+                   { "name"       : "Kc3",
+                     "format"     : "guint64" },
+                   { "name"       : "N",
+                     "format"     : "guint32" } ] }
+
+]
diff --git a/data/mbim-service-basic-connect.json b/data/mbim-service-basic-connect.json
new file mode 100644
index 0000000..7a32409
--- /dev/null
+++ b/data/mbim-service-basic-connect.json
@@ -0,0 +1,796 @@
+
+[
+  // *********************************************************************************
+  { "type" : "Service",
+    "name" : "Basic Connect" },
+
+  // *********************************************************************************
+  { "name"     : "Device Caps",
+    "service"  : "Basic Connect",
+    "type"     : "Command",
+    "query"    : [],
+    "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" : "MbimDataClass" },
+                   { "name"          : "SmsCaps",
+                     "format"        : "guint32",
+                     "public-format" : "MbimSmsCaps" },
+                   { "name"          : "ControlCaps",
+                     "format"        : "guint32",
+                     "public-format" : "MbimCtrlCaps" },
+                   { "name"   : "MaxSessions",
+                     "format" : "guint32" },
+                   { "name"     : "CustomDataClass",
+                     "format"   : "string",
+                     "max-size" : "22" },
+                   { "name"     : "DeviceId",
+                     "format"   : "string",
+                     "max-size" : "36" },
+                   { "name"     : "FirmwareInfo",
+                     "format"   : "string",
+                     "max-size" : "60" },
+                   { "name"     : "HardwareInfo",
+                     "format"   : "string",
+                     "max-size" : "60" } ] },
+
+  // *********************************************************************************
+  { "name"     : "Subscriber Ready Status",
+    "service"  : "Basic Connect",
+    "type"     : "Command",
+    "query"    : [],
+    "response" : [ { "name"          : "ReadyState",
+                     "format"        : "guint32",
+                     "public-format" : "MbimSubscriberReadyState" },
+                   { "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"         : "Radio State",
+    "service"      : "Basic Connect",
+    "type"         : "Command",
+    "set"          : [ { "name"          : "RadioState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimRadioSwitchState" } ],
+    "query"        : [],
+    "response"     : [ { "name"          : "HwRadioState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimRadioSwitchState" },
+			           { "name"          : "SwRadioState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimRadioSwitchState" } ],
+    "notification" : [ { "name"          : "HwRadioState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimRadioSwitchState" },
+			           { "name"          : "SwRadioState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimRadioSwitchState" } ] },
+
+  // *********************************************************************************
+  { "name"     : "Pin",
+    "service"  : "Basic Connect",
+    "type"     : "Command",
+    "set"      : [ { "name"          : "PinType",
+                     "format"        : "guint32",
+                     "public-format" : "MbimPinType" },
+                   { "name"          : "PinOperation",
+                     "format"        : "guint32",
+                     "public-format" : "MbimPinOperation" },
+                   { "name"   : "Pin",
+                     "format" : "string" },
+                   { "name"   : "NewPin",
+                     "format" : "string" } ],
+    "query"    : [],
+    "response" : [ { "name"          : "PinType",
+                     "format"        : "guint32",
+                     "public-format" : "MbimPinType" },
+                   { "name"          : "PinState",
+                     "format"        : "guint32",
+                     "public-format" : "MbimPinState" },
+                   { "name"   : "RemainingAttempts",
+                     "format" : "guint32" } ] },
+
+  // *********************************************************************************
+  { "name"     : "MbimPinDesc",
+    "type"     : "Struct",
+    "contents" : [ { "name"          : "PinMode",
+                     "format"        : "guint32",
+                     "public-format" : "MbimPinMode" },
+                   { "name"          : "PinFormat",
+                     "format"        : "guint32",
+                     "public-format" : "MbimPinFormat" },
+                   { "name"   : "PinLengthMin",
+                     "format" : "guint32" },
+                   { "name"   : "PinLengthMax",
+                     "format" : "guint32" } ] },
+
+  { "name"     : "Pin List",
+    "service"  : "Basic Connect",
+    "type"     : "Command",
+    "query"    : [],
+    "response" : [ { "name"        : "PinDescPin1",
+                     "format"      : "struct",
+                     "struct-type" : "MbimPinDesc" },
+                   { "name"        : "PinDescPin2",
+                     "format"      : "struct",
+                     "struct-type" : "MbimPinDesc" },
+                   { "name"        : "PinDescDeviceSimPin",
+                     "format"      : "struct",
+                     "struct-type" : "MbimPinDesc" },
+                   { "name"        : "PinDescDeviceFirstSimPin",
+                     "format"      : "struct",
+                     "struct-type" : "MbimPinDesc" },
+                   { "name"        : "PinDescNetworkPin",
+                     "format"      : "struct",
+                     "struct-type" : "MbimPinDesc" },
+                   { "name"        : "PinDescNetworkSubsetPin",
+                     "format"      : "struct",
+                     "struct-type" : "MbimPinDesc" },
+                   { "name"        : "PinDescServiceProviderPin",
+                     "format"      : "struct",
+                     "struct-type" : "MbimPinDesc" },
+                   { "name"        : "PinDescCorporatePin",
+                     "format"      : "struct",
+                     "struct-type" : "MbimPinDesc" },
+                   { "name"        : "PinDescSubsidyLock",
+                     "format"      : "struct",
+                     "struct-type" : "MbimPinDesc" },
+                   { "name"        : "PinDescCustom",
+                     "format"      : "struct",
+                     "struct-type" : "MbimPinDesc" } ] },
+
+  // *********************************************************************************
+  { "name"     : "MbimProvider",
+    "type"     : "Struct",
+    "contents" : [ { "name"   : "ProviderId",
+                     "format" : "string" },
+                   { "name"          : "ProviderState",
+                     "format"        : "guint32",
+                     "public-format" : "MbimProviderState" },
+                   { "name"   : "ProviderName",
+                     "format" : "string" },
+                   { "name"          : "CellularClass",
+                     "format"        : "guint32",
+                     "public-format" : "MbimCellularClass" },
+                   { "name"   : "RSSI",
+                     "format" : "guint32" },
+                   { "name"   : "ErrorRate",
+                     "format" : "guint32" } ] },
+
+  { "name"     : "Home Provider",
+    "service"  : "Basic Connect",
+    "type"     : "Command",
+    "set"      : [ { "name"        : "Provider",
+                     "format"      : "struct" ,
+                     "struct-type" : "MbimProvider" } ],
+    "query"    : [],
+    "response" : [ { "name"        : "Provider",
+                     "format"      : "struct",
+                     "struct-type" : "MbimProvider" } ] },
+
+  // *********************************************************************************
+  { "name"       : "Preferred Providers",
+    "service"    : "Basic Connect",
+    "type"       : "Command",
+    "set"        : [ { "name"   : "ProvidersCount",
+                       "format" : "guint32" },
+                     { "name"             : "Providers",
+                       "format"           : "ref-struct-array" ,
+                       "struct-type"      : "MbimProvider",
+                       "array-size-field" : "ProvidersCount" } ],
+    "query"      : [],
+    "response"   : [ { "name"   : "ProvidersCount",
+                       "format" : "guint32" },
+                     { "name"             : "Providers",
+                       "format"           : "ref-struct-array" ,
+                       "struct-type"      : "MbimProvider",
+                       "array-size-field" : "ProvidersCount" } ],
+    "indication" : [ { "name"   : "ProvidersCount",
+                       "format" : "guint32" },
+                     { "name"             : "Providers",
+                       "format"           : "ref-struct-array" ,
+                       "struct-type"      : "MbimProvider",
+                       "array-size-field" : "ProvidersCount" } ] },
+
+  // *********************************************************************************
+  { "name"       : "Visible Providers",
+    "service"    : "Basic Connect",
+    "type"       : "Command",
+    "query"      : [ { "name" : "Action",
+                       "format": "guint32",
+                       "public-format" : "MbimVisibleProvidersAction" } ],
+    "response"   : [ { "name"   : "ProvidersCount",
+                       "format" : "guint32" },
+                     { "name"             : "Providers",
+                       "format"           : "ref-struct-array" ,
+                       "struct-type"      : "MbimProvider",
+                       "array-size-field" : "ProvidersCount" } ] },
+
+  // *********************************************************************************
+  { "name"         : "Register State",
+    "service"      : "Basic Connect",
+    "type"         : "Command",
+    "set"          : [ { "name"   : "ProviderId",
+                         "format" : "string" },
+                       { "name"          : "RegisterAction",
+                         "format"        : "guint32",
+                         "public-format" : "MbimRegisterAction" },
+                       { "name"          : "DataClass",
+                         "format"        : "guint32",
+                         "public-format" : "MbimDataClass" } ],
+    "query"        : [],
+    "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" } ],
+    "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"         : "Signal State",
+    "service"      : "Basic Connect",
+    "type"         : "Command",
+    "set"          : [ { "name"   : "SignalStrengthInterval",
+                         "format" : "guint32" },
+                       { "name"   : "RssiThreshold",
+                         "format" : "guint32" },
+                       { "name"   : "ErrorRateThreshold",
+                         "format" : "guint32" } ],
+    "query"        : [],
+    "response"     : [ { "name"   : "Rssi",
+                         "format" : "guint32" },
+                       { "name"   : "ErrorRate",
+                         "format" : "guint32" },
+                       { "name"   : "SignalStrengthInterval",
+                         "format" : "guint32" },
+                       { "name"   : "RssiThreshold",
+                         "format" : "guint32" },
+                       { "name"   : "ErrorRateThreshold",
+                         "format" : "guint32" } ],
+    "notification" : [ { "name"   : "Rssi",
+                         "format" : "guint32" },
+                       { "name"   : "ErrorRate",
+                         "format" : "guint32" },
+                       { "name"   : "SignalStrengthInterval",
+                         "format" : "guint32" },
+                       { "name"   : "RssiThreshold",
+                         "format" : "guint32" },
+                       { "name"   : "ErrorRateThreshold",
+                         "format" : "guint32" } ] },
+
+
+  // *********************************************************************************
+  { "name"         : "Packet Service",
+    "service"      : "Basic Connect",
+    "type"         : "Command",
+    "set"          : [ { "name"          : "PacketServiceAction",
+                         "format"        : "guint32",
+                         "public-format" : "MbimPacketServiceAction" } ],
+    "query"        : [],
+    "response"     : [ { "name"   : "NwError",
+                         "format" : "guint32" },
+                       { "name"          : "PacketServiceState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimPacketServiceState" },
+                       { "name"          : "HighestAvailableDataClass",
+                         "format"        : "guint32",
+                         "public-format" : "MbimDataClass" },
+                       { "name"   : "UplinkSpeed",
+                         "format" : "guint64" },
+                       { "name"   : "DownlinkSpeed",
+                         "format" : "guint64" } ],
+    "notification" : [ { "name"   : "NwError",
+                         "format" : "guint32" },
+                       { "name"          : "PacketServiceState",
+                         "format"        : "guint32",
+                         "public-format" : "MbimPacketServiceState" },
+                       { "name"          : "HighestAvailableDataClass",
+                         "format"        : "guint32",
+                         "public-format" : "MbimDataClass" },
+                       { "name"   : "UplinkSpeed",
+                         "format" : "guint64" },
+                       { "name"   : "DownlinkSpeed",
+                         "format" : "guint64" } ] },
+
+  // *********************************************************************************
+  { "name"         : "Connect",
+    "service"      : "Basic Connect",
+    "type"         : "Command",
+    "set"          : [ { "name"   : "SessionId",
+                         "format" : "guint32" },
+                       { "name"          : "ActivationCommand",
+                         "format"        : "guint32",
+                         "public-format" : "MbimActivationCommand" },
+                       { "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"          : "IpType",
+                         "format"        : "guint32",
+                         "public-format" : "MbimContextIpType" },
+                       { "name"   : "ContextType",
+                         "format" : "uuid" } ],
+    "query"        : [ { "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" } ],
+    "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" } ],
+    "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"     : "MbimProvisionedContextElement",
+    "type"     : "Struct",
+    "contents" : [ { "name"   : "ContextId",
+                     "format" : "guint32" },
+                   { "name"   : "ContextType",
+                     "format" : "uuid" },
+                   { "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",
+    "service"      : "Basic Connect",
+    "type"         : "Command",
+    "set"          : [ { "name"   : "ContextId",
+                         "format" : "guint32" },
+                       { "name"   : "ContextType",
+                         "format" : "uuid" },
+                       { "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"   : "ProviderId",
+                         "format" : "string" } ],
+    "query"        : [],
+    "response"     : [ { "name"   : "ProvisionedContextsCount",
+                         "format" : "guint32" },
+                       { "name"             : "ProvisionedContexts",
+                         "format"           : "ref-struct-array",
+                         "struct-type"      : "MbimProvisionedContextElement",
+                         "array-size-field" : "ProvisionedContextsCount" } ],
+    "notification" : [ { "name"   : "ProvisionedContextsCount",
+                         "format" : "guint32" },
+                       { "name"             : "ProvisionedContexts",
+                         "format"           : "ref-struct-array",
+                         "struct-type"      : "MbimProvisionedContextElement",
+                         "array-size-field" : "ProvisionedContextsCount" } ] },
+
+  // *********************************************************************************
+
+  { "name"     : "Service Activation",
+    "service"  : "Basic Connect",
+    "type"     : "Command",
+    "set"      : [ { "name"   : "Buffer",
+                     "format" : "unsized-byte-array" } ],
+    "response" : [ { "name"          : "NwError",
+                     "format"        : "guint32",
+                     "public-format" : "MbimNwError" },
+                   { "name"   : "Buffer",
+                     "format" : "unsized-byte-array" } ] },
+
+  // *********************************************************************************
+  { "name"     : "MbimIPv4Element",
+    "type"     : "Struct",
+    "contents" : [ { "name"   : "OnLinkPrefixLength",
+                     "format" : "guint32" },
+                   { "name"   : "IPv4Address",
+                     "format" : "ipv4" } ] },
+
+  { "name"     : "MbimIPv6Element",
+    "type"     : "Struct",
+    "contents" : [ { "name"   : "OnLinkPrefixLength",
+                     "format" : "guint32" },
+                   { "name"   : "IPv6Address",
+                     "format" : "ipv6" } ] },
+
+  { "name"         : "IP Configuration",
+    "service"      : "Basic Connect",
+    "type"         : "Command",
+    "query"        : [ { "name"   : "SessionId",
+                         "format" : "guint32" },
+                       { "name"          : "IPv4ConfigurationAvailable",
+                         "format"        : "guint32",
+                         "public-format" : "MbimIPConfigurationAvailableFlag" },
+                       { "name"          : "IPv6ConfigurationAvailable",
+                         "format"        : "guint32",
+                         "public-format" : "MbimIPConfigurationAvailableFlag" },
+                       { "name"   : "IPv4AddressCount",
+                         "format" : "guint32" },
+                       { "name"             : "IPv4Address",
+                         "format"           : "struct-array",
+                         "struct-type"      : "MbimIPv4Element",
+                         "array-size-field" : "IPv4AddressCount" },
+                       { "name"   : "IPv6AddressCount",
+                         "format" : "guint32" },
+                       { "name"             : "IPv6Address",
+                         "format"           : "struct-array",
+                         "struct-type"      : "MbimIPv6Element",
+                         "array-size-field" : "IPv6AddressCount" },
+                       { "name"   : "IPv4Gateway",
+                         "format" : "ref-ipv4" },
+                       { "name"   : "IPv6Gateway",
+                         "format" : "ref-ipv6" },
+                       { "name"   : "IPv4DnsServerCount",
+                         "format" : "guint32" },
+                       { "name"             : "IPv4DnsServer",
+                         "format"           : "ipv4-array",
+                         "array-size-field" : "IPv4DnsServerCount" },
+                       { "name"   : "IPv6DnsServerCount",
+                         "format" : "guint32" },
+                       { "name"             : "IPv6DnsServer",
+                         "format"           : "ipv6-array",
+                         "array-size-field" : "IPv6DnsServerCount" },
+                       { "name"   : "IPv4Mtu",
+                         "format" : "guint32" },
+                       { "name"   : "IPv6Mtu",
+                         "format" : "guint32" } ],
+    "response"     : [ { "name"   : "SessionId",
+                         "format" : "guint32" },
+                       { "name"          : "IPv4ConfigurationAvailable",
+                         "format"        : "guint32",
+                         "public-format" : "MbimIPConfigurationAvailableFlag" },
+                       { "name"          : "IPv6ConfigurationAvailable",
+                         "format"        : "guint32",
+                         "public-format" : "MbimIPConfigurationAvailableFlag" },
+                       { "name"   : "IPv4AddressCount",
+                         "format" : "guint32" },
+                       { "name"             : "IPv4Address",
+                         "format"           : "struct-array",
+                         "struct-type"      : "MbimIPv4Element",
+                         "array-size-field" : "IPv4AddressCount" },
+                       { "name"   : "IPv6AddressCount",
+                         "format" : "guint32" },
+                       { "name"             : "IPv6Address",
+                         "format"           : "struct-array",
+                         "struct-type"      : "MbimIPv6Element",
+                         "array-size-field" : "IPv6AddressCount" },
+                       { "name"   : "IPv4Gateway",
+                         "format" : "ref-ipv4" },
+                       { "name"   : "IPv6Gateway",
+                         "format" : "ref-ipv6" },
+                       { "name"   : "IPv4DnsServerCount",
+                         "format" : "guint32" },
+                       { "name"             : "IPv4DnsServer",
+                         "format"           : "ipv4-array",
+                         "array-size-field" : "IPv4DnsServerCount" },
+                       { "name"   : "IPv6DnsServerCount",
+                         "format" : "guint32" },
+                       { "name"             : "IPv6DnsServer",
+                         "format"           : "ipv6-array",
+                         "array-size-field" : "IPv6DnsServerCount" },
+                       { "name"   : "IPv4Mtu",
+                         "format" : "guint32" },
+                       { "name"   : "IPv6Mtu",
+                         "format" : "guint32" } ],
+    "notification" : [ { "name"   : "SessionId",
+                         "format" : "guint32" },
+                       { "name"          : "IPv4ConfigurationAvailable",
+                         "format"        : "guint32",
+                         "public-format" : "MbimIPConfigurationAvailableFlag" },
+                       { "name"          : "IPv6ConfigurationAvailable",
+                         "format"        : "guint32",
+                         "public-format" : "MbimIPConfigurationAvailableFlag" },
+                       { "name"   : "IPv4AddressCount",
+                         "format" : "guint32" },
+                       { "name"             : "IPv4Address",
+                         "format"           : "struct-array",
+                         "struct-type"      : "MbimIPv4Element",
+                         "array-size-field" : "IPv4AddressCount" },
+                       { "name"   : "IPv6AddressCount",
+                         "format" : "guint32" },
+                       { "name"             : "IPv6Address",
+                         "format"           : "struct-array",
+                         "struct-type"      : "MbimIPv6Element",
+                         "array-size-field" : "IPv6AddressCount" },
+                       { "name"   : "IPv4Gateway",
+                         "format" : "ref-ipv4" },
+                       { "name"   : "IPv6Gateway",
+                         "format" : "ref-ipv6" },
+                       { "name"   : "IPv4DnsServerCount",
+                         "format" : "guint32" },
+                       { "name"             : "IPv4DnsServer",
+                         "format"           : "ipv4-array",
+                         "array-size-field" : "IPv4DnsServerCount" },
+                       { "name"   : "IPv6DnsServerCount",
+                         "format" : "guint32" },
+                       { "name"             : "IPv6DnsServer",
+                         "format"           : "ipv6-array",
+                         "array-size-field" : "IPv6DnsServerCount" },
+                       { "name"   : "IPv4Mtu",
+                         "format" : "guint32" },
+                       { "name"   : "IPv6Mtu",
+                         "format" : "guint32" } ] },
+
+  // *********************************************************************************
+  { "name"     : "MbimDeviceServiceElement",
+    "type"     : "Struct",
+    "contents" : [ { "name"   : "DeviceServiceId",
+                     "format" : "uuid" },
+                   { "name"   : "DssPayload",
+                     "format" : "guint32" },
+                   { "name"   : "MaxDssInstances",
+                     "format" : "guint32" },
+                   { "name"       : "CidsCount",
+                     "format"     : "guint32" },
+                   { "name"             : "Cids",
+                     "format"           : "guint32-array",
+                     "array-size-field" : "CidsCount" } ] },
+
+  { "name"     : "Device Services",
+    "service"  : "Basic Connect",
+    "type"     : "Command",
+    "query"    : [],
+    "response" : [ { "name"   : "DeviceServicesCount",
+                     "format" : "guint32" },
+                   { "name"   : "MaxDssSessions",
+                     "format" : "guint32" },
+                   { "name"             : "DeviceServices",
+                     "format"           : "ref-struct-array",
+                     "struct-type"      : "MbimDeviceServiceElement",
+                     "array-size-field" : "DeviceServicesCount" } ] },
+
+  // *********************************************************************************
+  { "name"     : "MbimEventEntry",
+    "type"     : "Struct",
+    "contents" : [ { "name"   : "DeviceServiceId",
+                     "format" : "uuid" },
+                   { "name"       : "CidsCount",
+                     "format"     : "guint32" },
+                   { "name"             : "Cids",
+                     "format"           : "guint32-array",
+                     "array-size-field" : "CidsCount" } ] },
+
+  { "name"     : "Device Service Subscriber List",
+    "service"  : "Basic Connect",
+    "type"     : "Command",
+    "set"      : [ { "name"   : "EventsCount",
+                     "format" : "guint32" },
+                   { "name"             : "Events",
+                     "format"           : "ref-struct-array",
+                     "struct-type"      : "MbimEventEntry",
+                     "array-size-field" : "EventsCount" } ],
+    "response" : [ { "name"   : "EventsCount",
+                     "format" : "guint32" },
+                   { "name"             : "Events",
+                     "format"           : "ref-struct-array",
+                     "struct-type"      : "MbimEventEntry",
+                     "array-size-field" : "EventsCount" } ] },
+
+  // *********************************************************************************
+  { "name"     : "Packet Statistics",
+    "service"  : "Basic Connect",
+    "type"     : "Command",
+    "query"    : [],
+    "response" : [ { "name"   : "InDiscards",
+                     "format" : "guint32" },
+                   { "name"   : "InErrors",
+                     "format" : "guint32" },
+                   { "name"   : "InOctets",
+                     "format" : "guint64" },
+                   { "name"   : "InPackets",
+                     "format" : "guint64" },
+                   { "name"   : "OutOctets",
+                     "format" : "guint64" },
+                   { "name"   : "OutPackets",
+                     "format" : "guint64" },
+                   { "name"   : "OutErrors",
+                     "format" : "guint32" },
+                   { "name"   : "OutDiscards",
+                     "format" : "guint32" } ] },
+
+  // *********************************************************************************
+  { "name"     : "Network Idle Hint",
+    "service"  : "Basic Connect",
+    "type"     : "Command",
+    "query"    : [],
+    "set"      : [ { "name"          : "State",
+                     "format"        : "guint32",
+                     "public-format" : "MbimNetworkIdleHintState" } ],
+    "response" : [ { "name"          : "State",
+                     "format"        : "guint32",
+                     "public-format" : "MbimNetworkIdleHintState" } ] },
+
+  // *********************************************************************************
+  { "name"     : "Emergency Mode",
+    "service"  : "Basic Connect",
+    "type"     : "Command",
+    "query"    : [],
+    "set"      : [ { "name"          : "State",
+                     "format"        : "guint32",
+                     "public-format" : "MbimEmergencyModeState" } ],
+    "response" : [ { "name"          : "State",
+                     "format"        : "guint32",
+                     "public-format" : "MbimEmergencyModeState" } ] },
+
+  // *********************************************************************************
+  { "name"     : "MbimPacketFilter",
+    "type"     : "Struct",
+    "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"     : "IP Packet Filters",
+    "service"  : "Basic Connect",
+    "type"     : "Command",
+    "query"    : [ { "name"   : "SessionId",
+                     "format" : "guint32" },
+                   { "name"   : "PacketFiltersCount",
+                     "format" : "guint32" },
+                   { "name"             : "PacketFilters",
+                     "format"           : "ref-struct-array",
+                     "struct-type"      : "MbimPacketFilter",
+                     "array-size-field" : "PacketFiltersCount" } ],
+    "set"      : [ { "name"   : "SessionId",
+                     "format" : "guint32" },
+                   { "name"   : "PacketFiltersCount",
+                     "format" : "guint32" },
+                   { "name"             : "PacketFilters",
+                     "format"           : "ref-struct-array",
+                     "struct-type"      : "MbimPacketFilter",
+                     "array-size-field" : "PacketFiltersCount" } ],
+    "response" : [ { "name"   : "SessionId",
+                     "format" : "guint32" },
+                   { "name"   : "PacketFiltersCount",
+                     "format" : "guint32" },
+                   { "name"             : "PacketFilters",
+                     "format"           : "ref-struct-array",
+                     "struct-type"      : "MbimPacketFilter",
+                     "array-size-field" : "PacketFiltersCount" } ] },
+
+  // *********************************************************************************
+  { "name"       : "Multicarrier Providers",
+    "service"    : "Basic Connect",
+    "type"       : "Command",
+    "set"        : [ { "name"   : "ProvidersCount",
+                       "format" : "guint32" },
+                     { "name"             : "Providers",
+                       "format"           : "ref-struct-array" ,
+                       "struct-type"      : "MbimProvider",
+                       "array-size-field" : "ProvidersCount" } ],
+    "query"      : [],
+    "response"   : [ { "name"   : "ProvidersCount",
+                       "format" : "guint32" },
+                     { "name"             : "Providers",
+                       "format"           : "ref-struct-array" ,
+                       "struct-type"      : "MbimProvider",
+                       "array-size-field" : "ProvidersCount" } ],
+    "indication" : [ { "name"   : "ProvidersCount",
+                       "format" : "guint32" },
+                     { "name"             : "Providers",
+                       "format"           : "ref-struct-array" ,
+                       "struct-type"      : "MbimProvider",
+                       "array-size-field" : "ProvidersCount" } ] }
+
+]
diff --git a/data/mbim-service-dss.json b/data/mbim-service-dss.json
new file mode 100644
index 0000000..6beb300
--- /dev/null
+++ b/data/mbim-service-dss.json
@@ -0,0 +1,20 @@
+
+[
+  // *********************************************************************************
+  { "type" : "Service",
+    "name" : "DSS" },
+
+  // *********************************************************************************
+  { "name"     : "Connect",
+    "service"  : "DSS",
+    "type"     : "Command",
+    "set"      : [ { "name"   : "DeviceServiceId",
+                     "format" : "uuid" },
+                   { "name"   : "DssSessionId",
+                     "format" : "guint32" },
+                   { "name"          : "DssLinkState",
+                     "format"        : "guint32",
+                     "public-format" : "MbimDssLinkState" } ],
+    "response" : [] }
+
+]
diff --git a/data/mbim-service-phonebook.json b/data/mbim-service-phonebook.json
new file mode 100644
index 0000000..dffa598
--- /dev/null
+++ b/data/mbim-service-phonebook.json
@@ -0,0 +1,89 @@
+
+[
+    // *********************************************************************************
+    { "type" : "Service",
+      "name" : "Phonebook" },
+
+    // *********************************************************************************
+    { "name"         : "Configuration",
+      "service"      : "Phonebook",
+      "type"         : "Command",
+      "query"        : [],
+      "response"     : [ { "name"          : "State",
+                           "format"        : "guint32",
+                           "public-format" : "MbimPhonebookState" },
+                         { "name"   : "NumberOfEntries",
+                           "format" : "guint32" },
+                         { "name"   : "UsedEntries",
+                           "format" : "guint32" },
+                         { "name"   : "MaxNumberLength",
+                           "format" : "guint32" },
+                         { "name"   : "MaxName",
+                           "format" : "guint32" } ],
+      "notification" : [ { "name"          : "State",
+                           "format"        : "guint32",
+                           "public-format" : "MbimPhonebookState" },
+                         { "name"   : "NumberOfEntries",
+                           "format" : "guint32" },
+                         { "name"   : "UsedEntries",
+                           "format" : "guint32" },
+                         { "name"   : "MaxNumberLength",
+                           "format" : "guint32" },
+                         { "name"   : "MaxName",
+                           "format" : "guint32" } ] },
+
+    // *********************************************************************************
+  { "name"     : "MbimPhonebookEntry",
+    "type"     : "Struct",
+    "contents" : [ { "name"   : "EntryIndex",
+                     "format" : "guint32" },
+                   { "name"   : "Number",
+                     "format" : "string" },
+                   { "name"   : "Name",
+                     "format" : "string" } ] },
+
+  { "name"     : "Read",
+    "service"  : "Phonebook",
+    "type"     : "Command",
+    "query"    : [ { "name"          : "FilterFlag",
+                     "format"        : "guint32",
+                     "public-format" : "MbimPhonebookFlag" },
+                   { "name"          : "FilterMessageIndex",
+                     "format"        : "guint32" } ],
+    "response" : [ { "name"   : "EntryCount",
+                     "format" : "guint32" },
+                   { "name"             : "Entries",
+                     "format"           : "ref-struct-array",
+                     "struct-type"      : "MbimPhonebookEntry",
+                     "array-size-field" : "EntryCount" } ] },
+
+    // *********************************************************************************
+
+  { "name"     : "Delete",
+    "service"  : "Phonebook",
+    "type"     : "Command",
+    "set"      : [ { "name"          : "FilterFlag",
+                     "format"        : "guint32",
+                     "public-format" : "MbimPhonebookFlag" },
+                   { "name"          : "FilterMessageIndex",
+                     "format"        : "guint32" } ],
+    "response" : [] },
+
+
+    // *********************************************************************************
+
+  { "name"     : "Write",
+    "service"  : "Phonebook",
+    "type"     : "Command",
+    "set"      : [ { "name"          : "SaveFlag",
+                     "format"        : "guint32",
+                     "public-format" : "MbimPhonebookWriteFlag" },
+                   { "name"   : "SaveIndex",
+                     "format" : "guint32" },
+                   { "name"   : "Number",
+                     "format" : "string" },
+                   { "name"   : "Name",
+                     "format" : "string" } ],
+    "response" : [] }
+
+]
diff --git a/data/mbim-service-sms.json b/data/mbim-service-sms.json
new file mode 100644
index 0000000..57e63c1
--- /dev/null
+++ b/data/mbim-service-sms.json
@@ -0,0 +1,181 @@
+
+[
+    // *********************************************************************************
+    { "type" : "Service",
+      "name" : "SMS" },
+
+    // *********************************************************************************
+    { "name"     : "Configuration",
+      "service"  : "SMS",
+      "type"     : "Command",
+      "set"      : [ { "name"          : "Format",
+                       "format"        : "guint32",
+                       "public-format" : "MbimSmsFormat" },
+                     { "name"   : "ScAddress",
+                       "format" : "string" } ],
+      "query"    : [],
+      "response" : [ { "name"          : "SmsStorageState",
+                       "format"        : "guint32",
+                       "public-format" : "MbimSmsStorageState" },
+                     { "name"          : "Format",
+                       "format"        : "guint32",
+                       "public-format" : "MbimSmsFormat" },
+                     { "name"   : "MaxMessages",
+                       "format" : "guint32" },
+                     { "name"   : "CdmaShortMessageSize",
+                       "format" : "guint32" },
+                     { "name"   : "ScAddress",
+                       "format" : "string" } ] },
+
+    // *********************************************************************************
+    { "name"     : "MbimSmsPduReadRecord",
+      "type"     : "Struct",
+      "contents" : [ { "name"   : "MessageIndex",
+                       "format" : "guint32" },
+                     { "name"          : "MessageStatus",
+                       "format"        : "guint32",
+                       "public-format" : "MbimSmsStatus" },
+                     { "name"   : "PduData",
+                       "format" : "ref-byte-array" } ] },
+
+    { "name"     : "MbimSmsCdmaReadRecord",
+      "type"     : "Struct",
+      "contents" : [ { "name"   : "MessageIndex",
+                       "format" : "guint32" },
+                     { "name"          : "MessageStatus",
+                       "format"        : "guint32",
+                       "public-format" : "MbimSmsStatus" },
+                     { "name"   : "Address",
+                       "format" : "string" },
+                     { "name"   : "Timestamp",
+                       "format" : "string" },
+                     { "name"          : "Encoding",
+                       "format"        : "guint32",
+                       "public-format" : "MbimSmsCdmaEncoding" },
+                     { "name"          : "Language",
+                       "format"        : "guint32",
+                       "public-format" : "MbimSmsCdmaLanguage" },
+                     { "name"   : "EncodedMessage",
+                       "format" : "ref-byte-array" },
+                     { "name"   : "EncodedMessageSizeInCharacters",
+                       "format" : "guint32" } ] },
+
+    { "name"         : "Read",
+      "service"      : "SMS",
+      "type"         : "Command",
+      "query"        : [ { "name"          : "Format",
+                           "format"        : "guint32",
+                           "public-format" : "MbimSmsFormat" },
+                         { "name"          : "Flag",
+                           "format"        : "guint32",
+                           "public-format" : "MbimSmsFlag" },
+                         { "name"   : "MessageIndex",
+                           "format" : "guint32" } ],
+      "response"     : [ { "name"          : "Format",
+                           "format"        : "guint32",
+                           "public-format" : "MbimSmsFormat" },
+                         { "name"   : "MessagesCount",
+                           "format" : "guint32" },
+                         { "name"             : "PduMessages",
+                           "format"           : "ref-struct-array" ,
+                           "struct-type"      : "MbimSmsPduReadRecord",
+                           "array-size-field" : "MessagesCount",
+                           "available-if"     : { "field"     : "Format",
+                                                  "operation" : "==",
+                                                  "value"     : "MBIM_SMS_FORMAT_PDU" } },
+                         { "name"             : "CdmaMessages",
+                           "format"           : "ref-struct-array" ,
+                           "struct-type"      : "MbimSmsCdmaReadRecord",
+                           "array-size-field" : "MessagesCount",
+                           "available-if"     : { "field"     : "Format",
+                                                  "operation" : "==",
+                                                  "value"     : "MBIM_SMS_FORMAT_CDMA" } } ],
+      "notification" : [ { "name"          : "Format",
+                           "format"        : "guint32",
+                           "public-format" : "MbimSmsFormat" },
+                         { "name"   : "MessagesCount",
+                           "format" : "guint32" },
+                         { "name"             : "PduMessages",
+                           "format"           : "ref-struct-array" ,
+                           "struct-type"      : "MbimSmsPduReadRecord",
+                           "array-size-field" : "MessagesCount",
+                           "available-if"     : { "field"     : "Format",
+                                                  "operation" : "==",
+                                                  "value"     : "MBIM_SMS_FORMAT_PDU" } },
+                         { "name"             : "CdmaMessages",
+                           "format"           : "ref-struct-array" ,
+                           "struct-type"      : "MbimSmsCdmaReadRecord",
+                           "array-size-field" : "MessagesCount",
+                           "available-if"     : { "field"     : "Format",
+                                                  "operation" : "==",
+                                                  "value"     : "MBIM_SMS_FORMAT_CDMA" } } ] },
+
+    // *********************************************************************************
+    { "name"     : "MbimSmsPduSendRecord",
+      "type"     : "Struct",
+      "contents" : [ { "name"   : "PduData",
+                       "format" : "ref-byte-array" } ] },
+
+    { "name"     : "MbimSmsCdmaSendRecord",
+      "type"     : "Struct",
+      "contents" : [ { "name"          : "Encoding",
+                       "format"        : "guint32",
+                       "public-format" : "MbimSmsCdmaEncoding" },
+                     { "name"          : "Language",
+                       "format"        : "guint32",
+                       "public-format" : "MbimSmsCdmaLanguage" },
+                     { "name"   : "Address",
+                       "format" : "string" },
+                     { "name"   : "EncodedMessage",
+                       "format" : "ref-byte-array" },
+                     { "name"   : "EncodedMessageSizeInCharacters",
+                       "format" : "guint32" } ] },
+
+    { "name"     : "Send",
+      "service"  : "SMS",
+      "type"     : "Command",
+      "set"      : [ { "name"          : "Format",
+                       "format"        : "guint32",
+                       "public-format" : "MbimSmsFormat" },
+                     { "name"         : "PduMessage",
+                       "format"       : "struct",
+                       "struct-type"  : "MbimSmsPduSendRecord",
+                       "available-if" : { "field"     : "Format",
+                                          "operation" : "==",
+                                          "value"     : "MBIM_SMS_FORMAT_PDU" } },
+                     { "name"         : "CdmaMessage",
+                       "format"       : "struct",
+                       "struct-type"  : "MbimSmsCdmaSendRecord",
+                       "available-if" : { "field"     : "Format",
+                                          "operation" : "==",
+                                          "value"     : "MBIM_SMS_FORMAT_CDMA" } } ],
+      "response" : [ { "name"   : "MessageReference",
+                       "format" : "guint32" } ] },
+
+    // *********************************************************************************
+    { "name"     : "Delete",
+      "service"  : "SMS",
+      "type"     : "Command",
+      "set"      : [ { "name"          : "Flag",
+                       "format"        : "guint32",
+                       "public-format" : "MbimSmsFlag" },
+                     { "name"   : "MessageIndex",
+                       "format" : "guint32" } ],
+      "response" : [] },
+
+    // *********************************************************************************
+    { "name"         : "Message Store Status",
+      "service"      : "SMS",
+      "type"         : "Command",
+      "query"        : [],
+      "response"     : [ { "name"          : "Flag",
+                           "format"        : "guint32",
+                           "public-format" : "MbimSmsStatusFlag" },
+                         { "name"   : "MessageIndex",
+                           "format" : "guint32" } ],
+      "notification" : [ { "name"          : "Flag",
+                           "format"        : "guint32",
+                           "public-format" : "MbimSmsStatusFlag" },
+                         { "name"   : "MessageIndex",
+                           "format" : "guint32" } ] }
+]
diff --git a/data/mbim-service-stk.json b/data/mbim-service-stk.json
new file mode 100644
index 0000000..2b9bbfe
--- /dev/null
+++ b/data/mbim-service-stk.json
@@ -0,0 +1,47 @@
+
+[
+    // *********************************************************************************
+    { "type" : "Service",
+      "name" : "STK" },
+
+  // *********************************************************************************
+  { "name"         : "Pac",
+    "service"      : "STK",
+    "type"         : "Command",
+    "query"        : [],
+    "set"          : [ { "name"       : "PacHostcontrol",
+                         "format"     : "byte-array",
+                         "array-size" : "32" } ],
+
+    "response"     : [ { "name"       : "PacSupport",
+                         "format"     : "byte-array",
+                         "array-size" : "256" } ],
+    "notification" : [ { "name"          : "PacType",
+                         "format"        : "guint32",
+                         "public-format" : "MbimStkPacType" },
+                       { "name"   : "DataBuffer",
+                         "format" : "unsized-byte-array" } ] },
+
+  // *********************************************************************************
+  { "name"     : "Terminal Response",
+    "service"  : "STK",
+    "type"     : "Command",
+    "set"      : [ { "name"   : "Response",
+                     "format" : "ref-byte-array-no-offset" } ],
+    "response" : [ { "name"   : "ResultData",
+                     "format" : "ref-byte-array" },
+                   { "name"   : "StatusWords",
+                     "format" : "guint32" } ] },
+
+  // *********************************************************************************
+  { "name"     : "Envelope",
+    "service"  : "STK",
+    "type"     : "Command",
+    "query"    : [],
+    "set"      : [ { "name"   : "Data",
+                     "format" : "unsized-byte-array" } ],
+    // This response is only for the 'query', the 'set' one is empty...
+    "response" : [ { "name"       : "EnvelopeSupport",
+                     "format"     : "byte-array",
+                     "array-size" : "32" } ] }
+]
diff --git a/data/mbim-service-ussd.json b/data/mbim-service-ussd.json
new file mode 100644
index 0000000..10471e1
--- /dev/null
+++ b/data/mbim-service-ussd.json
@@ -0,0 +1,38 @@
+
+[
+    // *********************************************************************************
+    { "type" : "Service",
+      "name" : "USSD" },
+
+    // *********************************************************************************
+    { "name"         : "",
+      "service"      : "USSD",
+      "type"         : "Command",
+      "set"          : [ { "name"          : "Action",
+                           "format"        : "guint32",
+                           "public-format" : "MbimUssdAction" },
+                         { "name"   : "DataCodingScheme",
+                           "format" : "guint32" },
+                         { "name"   : "Payload",
+                           "format" : "ref-byte-array" } ],
+      "response"     : [ { "name"          : "Response",
+                           "format"        : "guint32",
+                           "public-format" : "MbimUssdResponse" },
+                         { "name"          : "SessionState",
+                           "format"        : "guint32",
+                           "public-format" : "MbimUssdSessionState" },
+                         { "name"   : "DataCodingScheme",
+                           "format" : "guint32" },
+                         { "name"   : "Payload",
+                           "format" : "ref-byte-array" } ],
+      "notification" : [ { "name"          : "Response",
+                           "format"        : "guint32",
+                           "public-format" : "MbimUssdResponse" },
+                         { "name"          : "SessionState",
+                           "format"        : "guint32",
+                           "public-format" : "MbimUssdSessionState" },
+                         { "name"   : "DataCodingScheme",
+                           "format" : "guint32" },
+                         { "name"   : "Payload",
+                           "format" : "ref-byte-array" } ] }
+]
diff --git a/data/pkg-config/Makefile.am b/data/pkg-config/Makefile.am
new file mode 100644
index 0000000..033081e
--- /dev/null
+++ b/data/pkg-config/Makefile.am
@@ -0,0 +1,4 @@
+
+# Set up pkg-config .pc files for exported libraries
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = mbim-glib.pc
diff --git a/data/pkg-config/mbim-glib.pc.in b/data/pkg-config/mbim-glib.pc.in
new file mode 100644
index 0000000..898375e
--- /dev/null
+++ b/data/pkg-config/mbim-glib.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: mbim-glib
+Description: Library to communicate with MBIM-powered modems
+Version: @VERSION@
+Requires: glib-2.0 gobject-2.0 gio-2.0
+Cflags: -I${includedir}/libmbim-glib
+Libs: -L${libdir} -lmbim-glib
diff --git a/docs/Makefile.am b/docs/Makefile.am
new file mode 100644
index 0000000..f3ddc22
--- /dev/null
+++ b/docs/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = reference
diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am
new file mode 100644
index 0000000..452e53d
--- /dev/null
+++ b/docs/reference/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = libmbim-glib
diff --git a/docs/reference/libmbim-glib/Makefile.am b/docs/reference/libmbim-glib/Makefile.am
new file mode 100644
index 0000000..07bcbe9
--- /dev/null
+++ b/docs/reference/libmbim-glib/Makefile.am
@@ -0,0 +1,100 @@
+
+# The name of the module.
+DOC_MODULE = libmbim-glib
+
+# The top-level SGML file.
+DOC_MAIN_SGML_FILE = $(DOC_MODULE)-docs.xml
+
+# Sections file building
+ALL_SECTIONS = \
+	$(srcdir)/libmbim-glib-common.sections \
+	$(top_builddir)/libmbim-glib/generated/mbim-basic-connect.sections \
+	$(top_builddir)/libmbim-glib/generated/mbim-sms.sections \
+	$(top_builddir)/libmbim-glib/generated/mbim-ussd.sections \
+	$(top_builddir)/libmbim-glib/generated/mbim-auth.sections \
+	$(top_builddir)/libmbim-glib/generated/mbim-phonebook.sections \
+	$(top_builddir)/libmbim-glib/generated/mbim-stk.sections \
+	$(top_builddir)/libmbim-glib/generated/mbim-dss.sections
+
+$(DOC_MODULE)-sections.mstamp: $(ALL_SECTIONS)
+	$(AM_V_GEN) \
+		rm -f $(DOC_MODULE)-sections.txt && \
+		cat $(ALL_SECTIONS) > $(DOC_MODULE)-sections.txt && \
+		touch $(DOC_MODULE)-sections.mstamp
+
+BUILT_SOURCES = $(DOC_MODULE)-sections.mstamp
+
+# Extra options to supply to gtkdoc-scan
+SCAN_OPTIONS = --rebuild-types
+
+# The directory containing the source code.
+DOC_SOURCE_DIR = \
+	$(top_srcdir)/libmbim-glib \
+	$(top_builddir)/libmbim-glib \
+	$(top_builddir)/libmbim-glib/generated
+
+# Used for dependencies
+HFILE_GLOB = \
+	$(top_srcdir)/libmbim-glib/*.h \
+	$(top_builddir)/libmbim-glib/*.h \
+	$(top_builddir)/libmbim-glib/generated/*.h
+CFILE_GLOB = \
+	$(top_srcdir)/libmbim-glib/*.c \
+	$(top_builddir)/libmbim-glib/generated/*.c
+
+# Headers to ignore
+IGNORE_HFILES = \
+	mbim-message-private.h
+
+# CFLAGS and LDFLAGS for compiling scan program. Only needed
+# if $(DOC_MODULE).types is non-empty.
+AM_CPPFLAGS = \
+	-I$(srcdir) \
+	-I$(top_srcdir) \
+	-I$(top_builddir) \
+	$(LIBMBIM_GLIB_CFLAGS)
+
+GTKDOC_LIBS = \
+	$(LIBMBIM_GLIB_LIBS) \
+	$(top_builddir)/libmbim-glib/libmbim-glib.la
+
+# Extra options to supply to gtkdoc-mkdb
+MKDB_OPTIONS = --output-format=xml --sgml-mode --name-space=mbim
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE)
+content_files = version.xml
+
+expand_content_files =
+
+extra_files =
+
+include $(top_srcdir)/gtk-doc.make
+
+
+EXTRA_DIST += \
+	libmbim-glib-common.sections \
+	$(DOC_MODULE)-sections.txt \
+	$(DOC_MODULE)-sections.mstamp \
+	version.xml.in \
+	$(DIAGRAMS) \
+	$(NULL)
+
+CLEANFILES += \
+	$(DOC_MODULE)-decl-list.txt \
+	$(DOC_MODULE)-decl.txt \
+	$(DOC_MODULE)-overrides.txt \
+	$(DOC_MODULE)-undeclared.txt \
+	$(DOC_MODULE)-undocumented.txt \
+	$(DOC_MODULE)-overrides.txt \
+	$(DOC_MODULE)-unused.txt \
+	$(DOC_MODULE).args \
+	$(DOC_MODULE).hierarchy \
+	$(DOC_MODULE).interfaces \
+	$(DOC_MODULE).prerequisites \
+	$(DOC_MODULE).signals \
+	$(DOC_MODULE).types \
+	$(DOC_MODULE)-sections.txt \
+	$(DOC_MODULE)-sections.mstamp \
+	*.stamp \
+	-rf xml html tmpl \
+	$(NULL)
diff --git a/docs/reference/libmbim-glib/libmbim-glib-common.sections b/docs/reference/libmbim-glib/libmbim-glib-common.sections
new file mode 100644
index 0000000..ded145e
--- /dev/null
+++ b/docs/reference/libmbim-glib/libmbim-glib-common.sections
@@ -0,0 +1,477 @@
+<SECTION>
+<FILE>mbim-version</FILE>
+<TITLE>Version checks</TITLE>
+MBIM_MAJOR_VERSION
+MBIM_MINOR_VERSION
+MBIM_MICRO_VERSION
+MBIM_CHECK_VERSION
+</SECTION>
+
+<SECTION>
+<FILE>mbim-uuid</FILE>
+MbimService
+MbimContextType
+MbimUuid
+MBIM_UUID_INVALID
+MBIM_UUID_BASIC_CONNECT
+MBIM_UUID_SMS
+MBIM_UUID_USSD
+MBIM_UUID_PHONEBOOK
+MBIM_UUID_STK
+MBIM_UUID_AUTH
+MBIM_UUID_DSS
+<SUBSECTION Methods>
+mbim_service_get_string
+mbim_context_type_get_string
+mbim_uuid_cmp
+mbim_uuid_get_printable
+mbim_uuid_from_service
+mbim_uuid_to_service
+mbim_uuid_from_context_type
+mbim_uuid_to_context_type
+<SUBSECTION Private>
+MBIM_PACKED
+mbim_service_build_string_from_mask
+mbim_context_type_build_string_from_mask
+</SECTION>
+
+<SECTION>
+<FILE>mbim-cid</FILE>
+MbimCidBasicConnect
+MbimCidSms
+MbimCidUssd
+MbimCidPhonebook
+MbimCidStk
+MbimCidAuth
+MbimCidDss
+<SUBSECTION Methods>
+mbim_cid_can_set
+mbim_cid_can_query
+mbim_cid_can_notify
+mbim_cid_get_printable
+mbim_cid_basic_connect_get_string
+mbim_cid_sms_get_string
+mbim_cid_ussd_get_string
+mbim_cid_phonebook_get_string
+mbim_cid_stk_get_string
+mbim_cid_auth_get_string
+mbim_cid_dss_get_string
+<SUBSECTION Private>
+mbim_cid_basic_connect_build_string_from_mask
+mbim_cid_sms_build_string_from_mask
+mbim_cid_ussd_build_string_from_mask
+mbim_cid_phonebook_build_string_from_mask
+mbim_cid_stk_build_string_from_mask
+mbim_cid_auth_build_string_from_mask
+mbim_cid_dss_build_string_from_mask
+<SUBSECTION Standard>
+MBIM_TYPE_CID_AUTH
+MBIM_TYPE_CID_BASIC_CONNECT
+MBIM_TYPE_CID_DSS
+MBIM_TYPE_CID_PHONEBOOK
+MBIM_TYPE_CID_SMS
+MBIM_TYPE_CID_STK
+MBIM_TYPE_CID_USSD
+mbim_cid_auth_get_type
+mbim_cid_basic_connect_get_type
+mbim_cid_dss_get_type
+mbim_cid_phonebook_get_type
+mbim_cid_sms_get_type
+mbim_cid_stk_get_type
+mbim_cid_ussd_get_type
+</SECTION>
+
+<SECTION>
+<FILE>mbim-message</FILE>
+MbimMessage
+MbimMessageType
+MbimIPv4
+MbimIPv6
+MbimMessageCommandType
+<SUBSECTION Methods>
+mbim_message_new
+mbim_message_dup
+mbim_message_ref
+mbim_message_unref
+mbim_message_get_printable
+mbim_message_get_raw
+mbim_message_get_message_type
+mbim_message_get_message_length
+mbim_message_get_transaction_id
+mbim_message_set_transaction_id
+mbim_message_type_get_string
+<SUBSECTION MethodsOpen>
+mbim_message_open_new
+mbim_message_open_get_max_control_transfer
+<SUBSECTION MethodsOpenDone>
+mbim_message_open_done_get_status_code
+mbim_message_open_done_get_result
+<SUBSECTION MethodsClose>
+mbim_message_close_new
+<SUBSECTION MethodsCloseDone>
+mbim_message_close_done_get_status_code
+mbim_message_close_done_get_result
+<SUBSECTION MethodsError>
+mbim_message_error_new
+mbim_message_error_get_error_status_code
+mbim_message_error_get_error
+<SUBSECTION MethodsCommand>
+mbim_message_command_new
+mbim_message_command_append
+mbim_message_command_get_service
+mbim_message_command_get_service_id
+mbim_message_command_get_cid
+mbim_message_command_get_command_type
+mbim_message_command_get_raw_information_buffer
+mbim_message_command_type_get_string
+<SUBSECTION MethodsCommandDone>
+mbim_message_command_done_get_service
+mbim_message_command_done_get_service_id
+mbim_message_command_done_get_cid
+mbim_message_command_done_get_status_code
+mbim_message_command_done_get_result
+mbim_message_command_done_get_raw_information_buffer
+<SUBSECTION MethodsIndicateStatus>
+mbim_message_indicate_status_get_service
+mbim_message_indicate_status_get_service_id
+mbim_message_indicate_status_get_cid
+mbim_message_indicate_status_get_raw_information_buffer
+<SUBSECTION Private>
+mbim_message_type_build_string_from_mask
+mbim_message_command_type_build_string_from_mask
+<SUBSECTION Standard>
+MBIM_TYPE_MESSAGE
+mbim_message_get_type
+</SECTION>
+
+<SECTION>
+<FILE>mbim-device</FILE>
+<TITLE>MbimDevice</TITLE>
+MbimDevice
+mbim_device_new
+mbim_device_new_finish
+mbim_device_get_file
+mbim_device_peek_file
+mbim_device_get_path
+mbim_device_get_path_display
+mbim_device_is_open
+mbim_device_open
+mbim_device_open_finish
+mbim_device_close
+mbim_device_close_finish
+mbim_device_close_force
+mbim_device_get_next_transaction_id
+mbim_device_command
+mbim_device_command_finish
+<SUBSECTION Private>
+MbimDeviceClass
+MBIM_DEVICE_FILE
+MBIM_DEVICE_IN_SESSION
+MBIM_DEVICE_TRANSACTION_ID
+MBIM_DEVICE_SIGNAL_INDICATE_STATUS
+MBIM_DEVICE_SIGNAL_ERROR
+<SUBSECTION Standard>
+MBIM_DEVICE
+MBIM_DEVICE_CLASS
+MBIM_DEVICE_GET_CLASS
+MBIM_IS_DEVICE
+MBIM_IS_DEVICE_CLASS
+MBIM_TYPE_DEVICE
+MbimDevicePrivate
+mbim_device_get_type
+</SECTION>
+
+<SECTION>
+<FILE>mbim-enums</FILE>
+MbimDeviceType
+MbimCellularClass
+MbimVoiceClass
+MbimSimClass
+MbimDataClass
+MbimSmsCaps
+MbimCtrlCaps
+MbimSubscriberReadyState
+MbimReadyInfoFlag
+MbimRadioSwitchState
+MbimPinType
+MbimPinState
+MbimPinOperation
+MbimPinMode
+MbimPinFormat
+MbimProviderState
+MbimVisibleProvidersAction
+MbimNwError
+MbimRegisterAction
+MbimRegisterState
+MbimRegisterMode
+MbimRegistrationFlag
+MbimPacketServiceAction
+MbimPacketServiceState
+MbimActivationCommand
+MbimCompression
+MbimAuthProtocol
+MbimContextIpType
+MbimActivationState
+MbimVoiceCallState
+MbimIPConfigurationAvailableFlag
+MbimSmsStorageState
+MbimSmsFormat
+MbimSmsFlag
+MbimSmsCdmaLang
+MbimSmsCdmaEncoding
+MbimSmsStatus
+MbimSmsStatusFlag
+MbimUssdAction
+MbimUssdResponse
+MbimUssdSessionState
+MbimPhonebookFlag
+MbimPhonebookState
+MbimPhonebookWriteFlag
+MbimStkPacProfile
+MbimStkPacType
+MbimNetworkIdleHintState
+MbimEmergencyModeState
+MbimDssLinkState
+<SUBSECTION Methods>
+mbim_device_type_get_string
+mbim_cellular_class_build_string_from_mask
+mbim_voice_class_get_string
+mbim_sim_class_build_string_from_mask
+mbim_data_class_build_string_from_mask
+mbim_sms_caps_build_string_from_mask
+mbim_ctrl_caps_build_string_from_mask
+mbim_subscriber_ready_state_get_string
+mbim_ready_info_flag_build_string_from_mask
+mbim_radio_switch_state_get_string
+mbim_pin_type_get_string
+mbim_pin_state_get_string
+mbim_pin_operation_get_string
+mbim_pin_mode_get_string
+mbim_pin_format_get_string
+mbim_provider_state_build_string_from_mask
+mbim_visible_providers_action_get_string
+mbim_nw_error_get_string
+mbim_register_action_get_string
+mbim_register_state_get_string
+mbim_register_mode_get_string
+mbim_packet_service_action_get_string
+mbim_packet_service_state_get_string
+mbim_activation_command_get_string
+mbim_compression_get_string
+mbim_auth_protocol_get_string
+mbim_context_ip_type_get_string
+mbim_activation_state_get_string
+mbim_voice_call_state_get_string
+mbim_ip_configuration_available_flag_build_string_from_mask
+mbim_sms_storage_state_get_string
+mbim_sms_format_get_string
+mbim_sms_flag_get_string
+mbim_sms_cdma_lang_get_string
+mbim_sms_cdma_encoding_get_string
+mbim_sms_status_get_string
+mbim_sms_status_flag_get_string
+mbim_ussd_action_get_string
+mbim_ussd_response_get_string
+mbim_ussd_session_state_get_string
+mbim_phonebook_flag_get_string
+mbim_phonebook_state_get_string
+mbim_phonebook_write_flag_get_string
+mbim_stk_pac_profile_get_string
+mbim_stk_pac_type_get_string
+mbim_network_idle_hint_state_get_string
+mbim_emergency_mode_state_get_string
+mbim_dss_link_state_get_string
+<SUBSECTION Private>
+mbim_device_type_build_string_from_mask
+mbim_cellular_class_get_string
+mbim_voice_class_build_string_from_mask
+mbim_sim_class_get_string
+mbim_data_class_get_string
+mbim_sms_caps_get_string
+mbim_ctrl_caps_get_string
+mbim_subscriber_ready_state_build_string_from_mask
+mbim_ready_info_flag_get_string
+mbim_radio_switch_state_build_string_from_mask
+mbim_pin_type_build_string_from_mask
+mbim_pin_state_build_string_from_mask
+mbim_pin_operation_build_string_from_mask
+mbim_pin_mode_build_string_from_mask
+mbim_pin_format_build_string_from_mask
+mbim_provider_state_get_string
+mbim_visible_providers_action_build_string_from_mask
+mbim_nw_error_build_string_from_mask
+mbim_register_action_build_string_from_mask
+mbim_register_state_build_string_from_mask
+mbim_register_mode_build_string_from_mask
+mbim_registration_flag_get_string
+mbim_registration_flag_build_string_from_mask
+mbim_packet_service_action_build_string_from_mask
+mbim_packet_service_state_build_string_from_mask
+mbim_activation_command_build_string_from_mask
+mbim_compression_build_string_from_mask
+mbim_auth_protocol_build_string_from_mask
+mbim_context_ip_type_build_string_from_mask
+mbim_activation_state_build_string_from_mask
+mbim_voice_call_state_build_string_from_mask
+mbim_ip_configuration_available_flag_get_string
+mbim_sms_cdma_encoding_build_string_from_mask
+mbim_sms_cdma_lang_build_string_from_mask
+mbim_sms_flag_build_string_from_mask
+mbim_sms_format_build_string_from_mask
+mbim_sms_status_build_string_from_mask
+mbim_sms_status_flag_build_string_from_mask
+mbim_sms_storage_state_build_string_from_mask
+mbim_ussd_action_build_string_from_mask
+mbim_ussd_response_build_string_from_mask
+mbim_ussd_session_state_build_string_from_mask
+mbim_phonebook_flag_build_string_from_mask
+mbim_phonebook_state_build_string_from_mask
+mbim_phonebook_write_flag_build_string_from_mask
+mbim_stk_pac_profile_build_string_from_mask
+mbim_stk_pac_type_build_string_from_mask
+mbim_network_idle_hint_state_build_string_from_mask
+mbim_emergency_mode_state_build_string_from_mask
+mbim_dss_link_state_build_string_from_mask
+<SUBSECTION Standard>
+MBIM_TYPE_ACTIVATION_COMMAND
+MBIM_TYPE_ACTIVATION_STATE
+MBIM_TYPE_AUTH_PROTOCOL
+MBIM_TYPE_CELLULAR_CLASS
+MBIM_TYPE_COMPRESSION
+MBIM_TYPE_CONTEXT_IP_TYPE
+MBIM_TYPE_CONTEXT_TYPE
+MBIM_TYPE_CTRL_CAPS
+MBIM_TYPE_DATA_CLASS
+MBIM_TYPE_DEVICE_TYPE
+MBIM_TYPE_IP_CONFIGURATION_AVAILABLE_FLAG
+MBIM_TYPE_MESSAGE_COMMAND_TYPE
+MBIM_TYPE_MESSAGE_TYPE
+MBIM_TYPE_NW_ERROR
+MBIM_TYPE_PACKET_SERVICE_ACTION
+MBIM_TYPE_PACKET_SERVICE_STATE
+MBIM_TYPE_PIN_FORMAT
+MBIM_TYPE_PIN_MODE
+MBIM_TYPE_PIN_OPERATION
+MBIM_TYPE_PIN_STATE
+MBIM_TYPE_PIN_TYPE
+MBIM_TYPE_PROVIDER_STATE
+MBIM_TYPE_VISIBLE_PROVIDERS_ACTION
+MBIM_TYPE_RADIO_SWITCH_STATE
+MBIM_TYPE_READY_INFO_FLAG
+MBIM_TYPE_REGISTER_ACTION
+MBIM_TYPE_REGISTER_MODE
+MBIM_TYPE_REGISTER_STATE
+MBIM_TYPE_REGISTRATION_FLAG
+MBIM_TYPE_SERVICE
+MBIM_TYPE_SIM_CLASS
+MBIM_TYPE_SMS_CAPS
+MBIM_TYPE_SUBSCRIBER_READY_STATE
+MBIM_TYPE_VOICE_CALL_STATE
+MBIM_TYPE_VOICE_CLASS
+MBIM_TYPE_SMS_CDMA_ENCODING
+MBIM_TYPE_SMS_CDMA_LANG
+MBIM_TYPE_SMS_FLAG
+MBIM_TYPE_SMS_FORMAT
+MBIM_TYPE_SMS_STATUS
+MBIM_TYPE_SMS_STATUS_FLAG
+MBIM_TYPE_SMS_STORAGE_STATE
+MBIM_TYPE_USSD_ACTION
+MBIM_TYPE_USSD_RESPONSE
+MBIM_TYPE_USSD_SESSION_STATE
+MBIM_TYPE_PHONEBOOK_FLAG
+MBIM_TYPE_PHONEBOOK_STATE
+MBIM_TYPE_PHONEBOOK_WRITE_FLAG
+MBIM_TYPE_STK_PAC_PROFILE
+MBIM_TYPE_STK_PAC_TYPE
+MBIM_TYPE_NETWORK_IDLE_HINT_STATE
+MBIM_TYPE_EMERGENCY_MODE_STATE
+MBIM_TYPE_DSS_LINK_STATE
+mbim_activation_command_get_type
+mbim_activation_state_get_type
+mbim_auth_protocol_get_type
+mbim_cellular_class_get_type
+mbim_compression_get_type
+mbim_context_ip_type_get_type
+mbim_context_type_get_type
+mbim_ctrl_caps_get_type
+mbim_data_class_get_type
+mbim_device_type_get_type
+mbim_ip_configuration_available_flag_get_type
+mbim_message_command_type_get_type
+mbim_message_type_get_type
+mbim_nw_error_get_type
+mbim_packet_service_action_get_type
+mbim_packet_service_state_get_type
+mbim_pin_format_get_type
+mbim_pin_mode_get_type
+mbim_pin_operation_get_type
+mbim_pin_state_get_type
+mbim_pin_type_get_type
+mbim_provider_state_get_type
+mbim_visible_providers_action_get_type
+mbim_radio_switch_state_get_type
+mbim_ready_info_flag_get_type
+mbim_register_action_get_type
+mbim_register_mode_get_type
+mbim_register_state_get_type
+mbim_registration_flag_get_type
+mbim_service_get_type
+mbim_sim_class_get_type
+mbim_sms_caps_get_type
+mbim_subscriber_ready_state_get_type
+mbim_voice_call_state_get_type
+mbim_voice_class_get_type
+mbim_sms_cdma_encoding_get_type
+mbim_sms_cdma_lang_get_type
+mbim_sms_flag_get_type
+mbim_sms_format_get_type
+mbim_sms_status_flag_get_type
+mbim_sms_status_get_type
+mbim_sms_storage_state_get_type
+mbim_ussd_action_get_type
+mbim_ussd_response_get_type
+mbim_ussd_session_state_get_type
+mbim_phonebook_flag_get_type
+mbim_phonebook_state_get_type
+mbim_phonebook_write_flag_get_type
+mbim_stk_pac_profile_get_type
+mbim_stk_pac_type_get_type
+mbim_network_idle_hint_state_get_type
+mbim_emergency_mode_state_get_type
+mbim_dss_link_state_get_type
+</SECTION>
+
+<SECTION>
+<FILE>mbim-errors</FILE>
+MbimCoreError
+MbimProtocolError
+MbimStatusError
+<SUBSECTION Methods>
+mbim_core_error_get_string
+mbim_protocol_error_get_string
+mbim_status_error_get_string
+<SUBSECTION Private>
+MBIM_DBUS_ERROR_PREFIX
+MBIM_CORE_ERROR_DBUS_PREFIX
+MBIM_PROTOCOL_ERROR_DBUS_PREFIX
+MBIM_STATUS_ERROR_DBUS_PREFIX
+mbim_core_error_quark
+mbim_protocol_error_quark
+mbim_status_error_quark
+<SUBSECTION Standard>
+MBIM_CORE_ERROR
+MBIM_PROTOCOL_ERROR
+MBIM_STATUS_ERROR
+MBIM_TYPE_CORE_ERROR
+MBIM_TYPE_PROTOCOL_ERROR
+MBIM_TYPE_STATUS_ERROR
+mbim_core_error_get_type
+mbim_protocol_error_get_type
+mbim_status_error_get_type
+</SECTION>
+
+<SECTION>
+<FILE>mbim-utils</FILE>
+mbim_utils_get_traces_enabled
+mbim_utils_set_traces_enabled
+</SECTION>
diff --git a/docs/reference/libmbim-glib/libmbim-glib-docs.xml b/docs/reference/libmbim-glib/libmbim-glib-docs.xml
new file mode 100644
index 0000000..1018fb8
--- /dev/null
+++ b/docs/reference/libmbim-glib/libmbim-glib-docs.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+  <!ENTITY version SYSTEM "version.xml">
+]>
+<book id="libmbim-glib">
+  <bookinfo>
+    <title>libmbim-glib Reference Manual</title>
+    <releaseinfo>for libmbim-glib &version;</releaseinfo>
+
+    <copyright>
+      <year>2013</year>
+      <holder>The libmbim-glib authors</holder>
+    </copyright>
+
+    <legalnotice>
+      <para>
+        Permission is granted to copy, distribute and/or modify this
+        document under the terms of the <citetitle>GNU Free
+        Documentation License</citetitle>, Version 1.3 or any later
+        version published by the Free Software Foundation with no
+        Invariant Sections, no Front-Cover Texts, and no Back-Cover
+        Texts. You may obtain a copy of the <citetitle>GNU Free
+        Documentation License</citetitle> from the Free Software
+        Foundation by visiting <ulink type="http"
+        url="http://www.fsf.org">their Web site</ulink> or by writing
+        to:
+        <address>
+          The Free Software Foundation, Inc.
+          <street>51 Franklin Street</street>, Suite 500
+          <city>Boston</city>, <state>MA</state> <postcode>02110-1335</postcode>
+          <country>USA</country>
+        </address>
+      </para>
+    </legalnotice>
+  </bookinfo>
+
+  <chapter>
+    <title>Core</title>
+    <xi:include href="xml/mbim-version.xml"/>
+    <xi:include href="xml/mbim-uuid.xml"/>
+    <xi:include href="xml/mbim-cid.xml"/>
+    <xi:include href="xml/mbim-message.xml"/>
+    <xi:include href="xml/mbim-device.xml"/>
+    <xi:include href="xml/mbim-enums.xml"/>
+    <xi:include href="xml/mbim-errors.xml"/>
+    <xi:include href="xml/mbim-utils.xml"/>
+  </chapter>
+
+  <chapter>
+    <title>Services</title>
+    <xi:include href="xml/mbim-basic-connect.xml"/>
+    <xi:include href="xml/mbim-sms.xml"/>
+    <xi:include href="xml/mbim-ussd.xml"/>
+    <xi:include href="xml/mbim-auth.xml"/>
+    <xi:include href="xml/mbim-phonebook.xml"/>
+    <xi:include href="xml/mbim-stk.xml"/>
+    <xi:include href="xml/mbim-dss.xml"/>
+  </chapter>
+
+  <chapter id="object-tree">
+    <title>Object Hierarchy</title>
+     <xi:include href="xml/tree_index.sgml"/>
+  </chapter>
+  <index id="api-index-full">
+    <title>API Index</title>
+    <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+  </index>
+  <index id="deprecated-api-index" role="deprecated">
+    <title>Index of deprecated API</title>
+    <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
+  </index>
+
+  <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+</book>
diff --git a/docs/reference/libmbim-glib/version.xml.in b/docs/reference/libmbim-glib/version.xml.in
new file mode 100644
index 0000000..d78bda9
--- /dev/null
+++ b/docs/reference/libmbim-glib/version.xml.in
@@ -0,0 +1 @@
+@VERSION@
diff --git a/gtester.make b/gtester.make
new file mode 100644
index 0000000..40348dc
--- /dev/null
+++ b/gtester.make
@@ -0,0 +1,91 @@
+
+GTESTER = gtester
+GTESTER_REPORT = gtester-report
+
+# initialize variables for unconditional += appending
+EXTRA_DIST =
+TEST_PROGS =
+
+### testing rules
+
+# test: run all tests in cwd and subdirs
+test: test-nonrecursive
+	@ for subdir in $(SUBDIRS) . ; do \
+	    test "$$subdir" = "." -o "$$subdir" = "po" || \
+	    ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \
+	  done
+
+# test-nonrecursive: run tests only in cwd
+test-nonrecursive: ${TEST_PROGS}
+	@test -z "${TEST_PROGS}" || G_DEBUG=gc-friendly MALLOC_CHECK_=2 MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) ${GTESTER} --verbose ${TEST_PROGS}
+
+# test-report: run tests in subdirs and generate report
+# perf-report: run tests in subdirs with -m perf and generate report
+# full-report: like test-report: with -m perf and -m slow
+test-report perf-report full-report:	${TEST_PROGS}
+	@test -z "${TEST_PROGS}" || { \
+	  case $@ in \
+	  test-report) test_options="-k";; \
+	  perf-report) test_options="-k -m=perf";; \
+	  full-report) test_options="-k -m=perf -m=slow";; \
+	  esac ; \
+	  if test -z "$$GTESTER_LOGDIR" ; then	\
+	    ${GTESTER} --verbose $$test_options -o test-report.xml ${TEST_PROGS} ; \
+	  elif test -n "${TEST_PROGS}" ; then \
+	    ${GTESTER} --verbose $$test_options -o `mktemp "$$GTESTER_LOGDIR/log-XXXXXX"` ${TEST_PROGS} ; \
+	  fi ; \
+	}
+	@ ignore_logdir=true ; \
+	  if test -z "$$GTESTER_LOGDIR" ; then \
+	    GTESTER_LOGDIR=`mktemp -d "\`pwd\`/.testlogs-XXXXXX"`; export GTESTER_LOGDIR ; \
+	    ignore_logdir=false ; \
+	  fi ; \
+	  if test -d "$(top_srcdir)/.git" ; then \
+	    REVISION=`git describe` ; \
+	  else \
+	    REVISION=$(VERSION) ; \
+	  fi ; \
+	  for subdir in $(SUBDIRS) . ; do \
+	    test "$$subdir" = "." -o "$$subdir" = "po" || \
+	    ( cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $@ ) || exit $? ; \
+	  done ; \
+	  $$ignore_logdir || { \
+	    echo '<?xml version="1.0"?>'              > $@.xml ; \
+	    echo '<report-collection>'               >> $@.xml ; \
+	    echo '<info>'                            >> $@.xml ; \
+	    echo '  <package>$(PACKAGE)</package>'   >> $@.xml ; \
+	    echo '  <version>$(VERSION)</version>'   >> $@.xml ; \
+	    echo "  <revision>$$REVISION</revision>" >> $@.xml ; \
+	    echo '</info>'                           >> $@.xml ; \
+	    for lf in `ls -L "$$GTESTER_LOGDIR"/.` ; do \
+	      sed '1,1s/^<?xml\b[^>?]*?>//' <"$$GTESTER_LOGDIR"/"$$lf" >> $@.xml ; \
+	    done ; \
+	    echo >> $@.xml ; \
+	    echo '</report-collection>' >> $@.xml ; \
+	    rm -rf "$$GTESTER_LOGDIR"/ ; \
+	    ${GTESTER_REPORT} --version 2>/dev/null 1>&2 ; test "$$?" != 0 || ${GTESTER_REPORT} $@.xml >$@.html ; \
+	  }
+.PHONY: test test-report perf-report full-report test-nonrecursive
+
+.PHONY: lcov genlcov lcov-clean
+# use recursive makes in order to ignore errors during check
+lcov:
+	-$(MAKE) $(AM_MAKEFLAGS) -k check
+	$(MAKE) $(AM_MAKEFLAGS) genlcov
+
+# we have to massage the lcov.info file slightly to hide the effect of libtool
+# placing the objects files in the .libs/ directory separate from the *.c
+# we also have to delete tests/.libs/libmoduletestplugin_*.gcda
+genlcov:
+	rm -f $(top_builddir)/tests/.libs/libmoduletestplugin_*.gcda
+	$(LTP) --directory $(top_builddir) --capture --output-file glib-lcov.info --test-name GLIB_PERF --no-checksum --compat-libtool
+	LANG=C $(LTP_GENHTML) --prefix $(top_builddir) --output-directory glib-lcov --title "GLib Code Coverage" --legend --show-details glib-lcov.info
+	@echo "file://$(abs_top_builddir)/glib-lcov/index.html"
+
+lcov-clean:
+	-$(LTP) --directory $(top_builddir) -z
+	-rm -rf glib-lcov.info glib-lcov
+	-find -name '*.gcda' -print | xargs rm
+
+# run tests in cwd as part of make check
+check-local: test-nonrecursive
diff --git a/libmbim-glib/Makefile.am b/libmbim-glib/Makefile.am
new file mode 100644
index 0000000..01ccc76
--- /dev/null
+++ b/libmbim-glib/Makefile.am
@@ -0,0 +1,48 @@
+
+SUBDIRS = generated . test
+
+lib_LTLIBRARIES = libmbim-glib.la
+
+libmbim_glib_la_CPPFLAGS = \
+	$(LIBMBIM_GLIB_CFLAGS) \
+	-I$(top_srcdir) \
+	-I$(top_builddir) \
+	-I$(top_srcdir)/libmbim-glib \
+	-I$(top_srcdir)/libmbim-glib/generated \
+	-I$(top_builddir)/libmbim-glib \
+	-I$(top_builddir)/libmbim-glib/generated \
+	-DLIBMBIM_GLIB_COMPILATION \
+	-DG_LOG_DOMAIN=\"Mbim\"
+
+libmbim_glib_la_SOURCES = \
+	libmbim-glib.h \
+	mbim-version.h \
+	mbim-errors.h \
+	mbim-enums.h \
+	mbim-utils.h mbim-utils.c \
+	mbim-uuid.h mbim-uuid.c \
+	mbim-cid.h mbim-cid.c \
+	mbim-message-private.h mbim-message.h mbim-message.c \
+	mbim-device.h mbim-device.c
+
+libmbim_glib_la_LIBADD = \
+	${top_builddir}/libmbim-glib/generated/libmbim-glib-generated.la \
+	$(LIBMBIM_GLIB_LIBS)
+
+libmbim_glib_la_LDFLAGS = \
+	-version-info $(MBIM_GLIB_LT_CURRENT):$(MBIM_GLIB_LT_REVISION):$(MBIM_GLIB_LT_AGE)
+
+includedir = @includedir@/libmbim-glib
+include_HEADERS = \
+	libmbim-glib.h \
+	mbim-version.h \
+	mbim-errors.h \
+	mbim-enums.h \
+	mbim-utils.h \
+	mbim-uuid.h \
+	mbim-cid.h \
+	mbim-message.h \
+	mbim-device.h
+
+EXTRA_DIST = \
+	mbim-version.h.in
diff --git a/libmbim-glib/generated/Makefile.am b/libmbim-glib/generated/Makefile.am
new file mode 100644
index 0000000..fd5876a
--- /dev/null
+++ b/libmbim-glib/generated/Makefile.am
@@ -0,0 +1,158 @@
+noinst_LTLIBRARIES = libmbim-glib-generated.la
+
+GENERATED_H = \
+	mbim-error-types.h \
+	mbim-enum-types.h \
+	mbim-basic-connect.h \
+	mbim-sms.h \
+	mbim-ussd.h \
+	mbim-auth.h \
+	mbim-phonebook.h \
+	mbim-stk.h \
+	mbim-dss.h
+
+GENERATED_C = \
+	mbim-error-types.c \
+	mbim-error-quarks.c \
+	mbim-enum-types.c \
+	mbim-basic-connect.c \
+	mbim-sms.c \
+	mbim-ussd.c \
+	mbim-auth.c \
+	mbim-phonebook.c \
+	mbim-stk.c \
+	mbim-dss.c
+
+GENERATED_SECTIONS = \
+	mbim-basic-connect.sections \
+	mbim-sms.sections \
+	mbim-ussd.sections \
+	mbim-auth.sections \
+	mbim-phonebook.sections \
+	mbim-stk.sections \
+	mbim-dss.sections
+
+# Error types
+mbim-error-types.h: $(top_srcdir)/libmbim-glib/mbim-errors.h $(top_srcdir)/build-aux/templates/mbim-error-types-template.h
+	$(AM_V_GEN) $(GLIB_MKENUMS) \
+		--fhead "#ifndef __LIBMBIM_GLIB_ERROR_TYPES_H__\n#define __LIBMBIM_GLIB_ERROR_TYPES_H__\n#include \"mbim-errors.h\"\n" \
+		--template $(top_srcdir)/build-aux/templates/mbim-error-types-template.h \
+		--ftail "#endif /* __LIBMBIM_GLIB_ERROR_TYPES_H__ */\n" \
+		$(top_srcdir)/libmbim-glib/mbim-errors.h > $@
+
+mbim-error-types.c: $(top_srcdir)/libmbim-glib/mbim-errors.h mbim-error-types.h $(top_srcdir)/build-aux/templates/mbim-error-types-template.c
+	$(AM_V_GEN) $(GLIB_MKENUMS) \
+		--fhead "#include \"mbim-errors.h\"\n#include \"mbim-error-types.h\"\n" \
+		--template $(top_srcdir)/build-aux/templates/mbim-error-types-template.c \
+		$(top_srcdir)/libmbim-glib/mbim-errors.h > $@
+
+mbim-error-quarks.c: $(top_srcdir)/libmbim-glib/mbim-errors.h mbim-error-types.h $(top_srcdir)/build-aux/templates/mbim-error-quarks-template.c
+	$(AM_V_GEN) $(GLIB_MKENUMS) \
+		--fhead "#include \"mbim-errors.h\"\n#include \"mbim-error-types.h\"\n" \
+		--template $(top_srcdir)/build-aux/templates/mbim-error-quarks-template.c \
+		$(top_srcdir)/libmbim-glib/mbim-errors.h > $@
+
+# Enum/Flag types
+ENUMS = \
+	$(top_srcdir)/libmbim-glib/mbim-uuid.h \
+	$(top_srcdir)/libmbim-glib/mbim-cid.h \
+	$(top_srcdir)/libmbim-glib/mbim-message.h \
+	$(top_srcdir)/libmbim-glib/mbim-enums.h
+mbim-enum-types.h:  $(ENUMS) $(top_srcdir)/build-aux/templates/mbim-enum-types-template.h
+	$(AM_V_GEN) $(GLIB_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 $(top_srcdir)/build-aux/templates/mbim-enum-types-template.h \
+		--ftail "#endif /* __LIBMBIM_GLIB_ENUM_TYPES_H__ */\n" \
+		$(ENUMS) > $@
+
+mbim-enum-types.c: $(ENUMS) mbim-enum-types.h $(top_srcdir)/build-aux/templates/mbim-enum-types-template.c
+	$(AM_V_GEN) $(GLIB_MKENUMS) \
+		--fhead "#include \"mbim-enum-types.h\"\n" \
+		--template $(top_srcdir)/build-aux/templates/mbim-enum-types-template.c \
+		$(ENUMS) > $@
+
+# Basic Connect service
+mbim-basic-connect.h mbim-basic-connect.c mbim-basic-connect.sections: $(top_srcdir)/data/mbim-service-basic-connect.json $(top_srcdir)/build-aux/mbim-codegen/*.py $(top_srcdir)/build-aux/mbim-codegen/mbim-codegen
+	$(AM_V_GEN)  \
+		rm -f mbim-basic-connect.h && \
+		rm -f mbim-basic-connect.c && \
+		$(top_srcdir)/build-aux/mbim-codegen/mbim-codegen \
+			--input $(top_srcdir)/data/mbim-service-basic-connect.json \
+			--output mbim-basic-connect
+
+# SMS service
+mbim-sms.h mbim-sms.c mbim-sms.sections: $(top_srcdir)/data/mbim-service-sms.json $(top_srcdir)/build-aux/mbim-codegen/*.py $(top_srcdir)/build-aux/mbim-codegen/mbim-codegen
+	$(AM_V_GEN)  \
+		rm -f mbim-sms.h && \
+		rm -f mbim-sms.c && \
+		$(top_srcdir)/build-aux/mbim-codegen/mbim-codegen \
+			--input $(top_srcdir)/data/mbim-service-sms.json \
+			--output mbim-sms
+
+# USSD service
+mbim-ussd.h mbim-ussd.c mbim-ussd.sections: $(top_srcdir)/data/mbim-service-ussd.json $(top_srcdir)/build-aux/mbim-codegen/*.py $(top_srcdir)/build-aux/mbim-codegen/mbim-codegen
+	$(AM_V_GEN)  \
+		rm -f mbim-ussd.h && \
+		rm -f mbim-ussd.c && \
+		$(top_srcdir)/build-aux/mbim-codegen/mbim-codegen \
+			--input $(top_srcdir)/data/mbim-service-ussd.json \
+			--output mbim-ussd
+
+# Auth service
+mbim-auth.h mbim-auth.c mbim-auth.sections: $(top_srcdir)/data/mbim-service-auth.json $(top_srcdir)/build-aux/mbim-codegen/*.py $(top_srcdir)/build-aux/mbim-codegen/mbim-codegen
+	$(AM_V_GEN)  \
+		rm -f mbim-auth.h && \
+		rm -f mbim-auth.c && \
+		$(top_srcdir)/build-aux/mbim-codegen/mbim-codegen \
+			--input $(top_srcdir)/data/mbim-service-auth.json \
+			--output mbim-auth
+
+# Phonebook service
+mbim-phonebook.h mbim-phonebook.c mbim-phonebook.sections: $(top_srcdir)/data/mbim-service-phonebook.json $(top_srcdir)/build-aux/mbim-codegen/*.py $(top_srcdir)/build-aux/mbim-codegen/mbim-codegen
+	$(AM_V_GEN)  \
+		rm -f mbim-phonebook.h && \
+		rm -f mbim-phonebook.c && \
+		$(top_srcdir)/build-aux/mbim-codegen/mbim-codegen \
+			--input $(top_srcdir)/data/mbim-service-phonebook.json \
+			--output mbim-phonebook
+
+# STK service
+mbim-stk.h mbim-stk.c mbim-stk.sections: $(top_srcdir)/data/mbim-service-stk.json $(top_srcdir)/build-aux/mbim-codegen/*.py $(top_srcdir)/build-aux/mbim-codegen/mbim-codegen
+	$(AM_V_GEN)  \
+		rm -f mbim-stk.h && \
+		rm -f mbim-stk.c && \
+		$(top_srcdir)/build-aux/mbim-codegen/mbim-codegen \
+			--input $(top_srcdir)/data/mbim-service-stk.json \
+			--output mbim-stk
+
+# DSS service
+mbim-dss.h mbim-dss.c mbim-dss.sections: $(top_srcdir)/data/mbim-service-dss.json $(top_srcdir)/build-aux/mbim-codegen/*.py $(top_srcdir)/build-aux/mbim-codegen/mbim-codegen
+	$(AM_V_GEN)  \
+		rm -f mbim-dss.h && \
+		rm -f mbim-dss.c && \
+		$(top_srcdir)/build-aux/mbim-codegen/mbim-codegen \
+			--input $(top_srcdir)/data/mbim-service-dss.json \
+			--output mbim-dss
+
+BUILT_SOURCES = $(GENERATED_H) $(GENERATED_C)
+
+nodist_libmbim_glib_generated_la_SOURCES = \
+	$(GENERATED_H) \
+	$(GENERATED_C)
+
+libmbim_glib_generated_la_CPPFLAGS = \
+	$(LIBMBIM_GLIB_CFLAGS) \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/libmbim-glib \
+	-I$(top_builddir)/libmbim-glib \
+	-DLIBMBIM_GLIB_COMPILATION \
+	-DG_LOG_DOMAIN=\"Mbim\" \
+	-Wno-unused-function
+
+libmbim_glib_generated_la_LIBADD = \
+	$(LIBMBIM_GLIB_LIBS)
+
+includedir = @includedir@/libmbim-glib
+nodist_include_HEADERS = $(GENERATED_H)
+
+CLEANFILES = $(GENERATED_H) $(GENERATED_C) $(GENERATED_SECTIONS)
diff --git a/libmbim-glib/libmbim-glib.h b/libmbim-glib/libmbim-glib.h
new file mode 100644
index 0000000..05cd71c
--- /dev/null
+++ b/libmbim-glib/libmbim-glib.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#ifndef _LIBMBIM_GLIB_H_
+#define _LIBMBIM_GLIB_H_
+
+#define __LIBMBIM_GLIB_H_INSIDE__
+
+/* libmbim-glib headers */
+
+#include "mbim-version.h"
+#include "mbim-utils.h"
+#include "mbim-uuid.h"
+#include "mbim-cid.h"
+#include "mbim-message.h"
+#include "mbim-device.h"
+#include "mbim-enums.h"
+
+/* generated */
+#include "mbim-enum-types.h"
+#include "mbim-error-types.h"
+#include "mbim-basic-connect.h"
+#include "mbim-sms.h"
+#include "mbim-ussd.h"
+#include "mbim-auth.h"
+#include "mbim-phonebook.h"
+#include "mbim-stk.h"
+#include "mbim-dss.h"
+
+#endif /* _LIBMBIM_GLIB_H_ */
diff --git a/libmbim-glib/mbim-cid.c b/libmbim-glib/mbim-cid.c
new file mode 100644
index 0000000..de86c93
--- /dev/null
+++ b/libmbim-glib/mbim-cid.c
@@ -0,0 +1,275 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include "mbim-cid.h"
+#include "mbim-enum-types.h"
+
+/**
+ * SECTION: mbim-cid
+ * @title: Command IDs
+ *
+ * This section defines the interface of the known command IDs.
+ */
+
+typedef struct {
+    gboolean set;
+    gboolean query;
+    gboolean notify;
+} CidConfig;
+
+/* Note: index of the array is CID-1 */
+#define MBIM_CID_BASIC_CONNECT_LAST MBIM_CID_BASIC_CONNECT_MULTICARRIER_PROVIDERS
+static const CidConfig cid_basic_connect_config [MBIM_CID_BASIC_CONNECT_LAST] = {
+    { FALSE, TRUE,  FALSE }, /* MBIM_CID_BASIC_CONNECT_DEVICE_CAPSo */
+    { FALSE, TRUE,  TRUE  }, /* MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS */
+    { TRUE,  TRUE,  TRUE  }, /* MBIM_CID_BASIC_CONNECT_RADIO_STATE */
+    { TRUE,  TRUE,  FALSE }, /* MBIM_CID_BASIC_CONNECT_PIN */
+    { FALSE, TRUE,  FALSE }, /* MBIM_CID_BASIC_CONNECT_PIN_LIST */
+    { TRUE,  TRUE,  FALSE }, /* MBIM_CID_BASIC_CONNECT_HOME_PROVIDER */
+    { TRUE,  TRUE,  TRUE  }, /* MBIM_CID_BASIC_CONNECT_PREFERRED_PROVIDERS */
+    { FALSE, TRUE,  FALSE }, /* MBIM_CID_BASIC_CONNECT_VISIBLE_PROVIDERS */
+    { TRUE,  TRUE,  TRUE  }, /* MBIM_CID_BASIC_CONNECT_REGISTER_STATE */
+    { TRUE,  TRUE,  TRUE  }, /* MBIM_CID_BASIC_CONNECT_PACKET_SERVICE */
+    { TRUE,  TRUE,  TRUE  }, /* MBIM_CID_BASIC_CONNECT_SIGNAL_STATE */
+    { TRUE,  TRUE,  TRUE  }, /* MBIM_CID_BASIC_CONNECT_CONNECT */
+    { TRUE,  TRUE,  TRUE  }, /* MBIM_CID_BASIC_CONNECT_PROVISIONED_CONTEXTS */
+    { TRUE,  FALSE, FALSE }, /* MBIM_CID_BASIC_CONNECT_SERVICE_ACTIVATION */
+    { FALSE, TRUE,  TRUE  }, /* MBIM_CID_BASIC_CONNECT_IP_CONFIGURATION */
+    { FALSE, TRUE,  FALSE }, /* MBIM_CID_BASIC_CONNECT_DEVICE_SERVICES */
+    { FALSE, FALSE, FALSE }, /* 17 reserved */
+    { FALSE, FALSE, FALSE }, /* 18 reserved */
+    { TRUE,  FALSE, FALSE }, /* MBIM_CID_BASIC_CONNECT_DEVICE_SERVICE_SUBSCRIBER_LIST */
+    { FALSE, TRUE,  FALSE }, /* MBIM_CID_BASIC_CONNECT_PACKET_STATISTICS */
+    { TRUE,  TRUE,  FALSE }, /* MBIM_CID_BASIC_CONNECT_NETWORK_IDLE_HINT */
+    { FALSE, TRUE,  TRUE  }, /* MBIM_CID_BASIC_CONNECT_EMERGENCY_MODE */
+    { TRUE,  TRUE,  FALSE }, /* MBIM_CID_BASIC_CONNECT_IP_PACKET_FILTERS */
+    { TRUE,  TRUE,  TRUE  }, /* MBIM_CID_BASIC_CONNECT_MULTICARRIER_PROVIDERS */
+};
+
+/* Note: index of the array is CID-1 */
+#define MBIM_CID_SMS_LAST MBIM_CID_SMS_MESSAGE_STORE_STATUS
+static const CidConfig cid_sms_config [MBIM_CID_SMS_LAST] = {
+    { TRUE,  TRUE,  TRUE  }, /* MBIM_CID_SMS_CONFIGURATION */
+    { FALSE, TRUE,  TRUE  }, /* MBIM_CID_SMS_READ */
+    { TRUE,  FALSE, FALSE }, /* MBIM_CID_SMS_SEND */
+    { TRUE,  FALSE, FALSE }, /* MBIM_CID_SMS_DELETE */
+    { FALSE, TRUE,  TRUE  }, /* MBIM_CID_SMS_MESSAGE_STORE_STATUS */
+};
+
+/* Note: index of the array is CID-1 */
+#define MBIM_CID_USSD_LAST MBIM_CID_USSD
+static const CidConfig cid_ussd_config [MBIM_CID_USSD_LAST] = {
+    { TRUE,  FALSE, TRUE  }, /* MBIM_CID_USSD */
+};
+
+/* Note: index of the array is CID-1 */
+#define MBIM_CID_PHONEBOOK_LAST MBIM_CID_PHONEBOOK_WRITE
+static const CidConfig cid_phonebook_config [MBIM_CID_PHONEBOOK_LAST] = {
+    { FALSE, TRUE,  TRUE  }, /* MBIM_CID_PHONEBOOK_CONFIGURATION */
+    { FALSE, TRUE,  FALSE }, /* MBIM_CID_PHONEBOOK_READ */
+    { TRUE,  FALSE, FALSE }, /* MBIM_CID_PHONEBOOK_DELETE */
+    { TRUE,  FALSE, FALSE }, /* MBIM_CID_PHONEBOOK_WRITE */
+};
+
+/* Note: index of the array is CID-1 */
+#define MBIM_CID_STK_LAST MBIM_CID_STK_ENVELOPE
+static const CidConfig cid_stk_config [MBIM_CID_STK_LAST] = {
+    { TRUE,  TRUE,  TRUE  }, /* MBIM_CID_STK_PAC */
+    { TRUE,  FALSE, FALSE }, /* MBIM_CID_STK_TERMINAL_RESPONSE */
+    { TRUE,  TRUE,  FALSE }, /* MBIM_CID_STK_ENVELOPE */
+};
+
+/* Note: index of the array is CID-1 */
+#define MBIM_CID_AUTH_LAST MBIM_CID_AUTH_SIM
+static const CidConfig cid_auth_config [MBIM_CID_AUTH_LAST] = {
+    { FALSE, TRUE,  FALSE }, /* MBIM_CID_AUTH_AKA */
+    { FALSE, TRUE,  FALSE }, /* MBIM_CID_AUTH_AKAP */
+    { FALSE, TRUE,  FALSE }, /* MBIM_CID_AUTH_SIM */
+};
+
+/* Note: index of the array is CID-1 */
+#define MBIM_CID_DSS_LAST MBIM_CID_DSS_CONNECT
+static const CidConfig cid_dss_config [MBIM_CID_DSS_LAST] = {
+    { TRUE,  FALSE, FALSE }, /* MBIM_CID_DSS_CONNECT */
+};
+
+/**
+ * mbim_cid_can_set:
+ * @service: a #MbimService.
+ * @cid: a command ID.
+ *
+ * Checks whether the given command allows setting.
+ *
+ * Returns: %TRUE if the command allows setting, %FALSE otherwise.
+ */
+gboolean
+mbim_cid_can_set (MbimService service,
+                  guint       cid)
+{
+    /* CID = 0 is never a valid command */
+    g_return_val_if_fail (cid > 0, FALSE);
+    /* Known service required */
+    g_return_val_if_fail (service > MBIM_SERVICE_INVALID, FALSE);
+    g_return_val_if_fail (service <= MBIM_SERVICE_DSS, FALSE);
+
+    switch (service) {
+    case MBIM_SERVICE_BASIC_CONNECT:
+        return cid_basic_connect_config[cid - 1].set;
+    case MBIM_SERVICE_SMS:
+        return cid_sms_config[cid - 1].set;
+    case MBIM_SERVICE_USSD:
+        return cid_ussd_config[cid - 1].set;
+    case MBIM_SERVICE_PHONEBOOK:
+        return cid_phonebook_config[cid - 1].set;
+    case MBIM_SERVICE_STK:
+        return cid_stk_config[cid - 1].set;
+    case MBIM_SERVICE_AUTH:
+        return cid_auth_config[cid - 1].set;
+    case MBIM_SERVICE_DSS:
+        return cid_dss_config[cid - 1].set;
+    default:
+        g_assert_not_reached ();
+        return FALSE;
+    }
+}
+
+/**
+ * mbim_cid_can_query:
+ * @service: a #MbimService.
+ * @cid: a command ID.
+ *
+ * Checks whether the given command allows querying.
+ *
+ * Returns: %TRUE if the command allows querying, %FALSE otherwise.
+ */
+gboolean
+mbim_cid_can_query (MbimService service,
+                    guint       cid)
+{
+    /* CID = 0 is never a valid command */
+    g_return_val_if_fail (cid > 0, FALSE);
+    /* Known service required */
+    g_return_val_if_fail (service > MBIM_SERVICE_INVALID, FALSE);
+    g_return_val_if_fail (service <= MBIM_SERVICE_DSS, FALSE);
+
+    switch (service) {
+    case MBIM_SERVICE_BASIC_CONNECT:
+        return cid_basic_connect_config[cid - 1].query;
+    case MBIM_SERVICE_SMS:
+        return cid_sms_config[cid - 1].query;
+    case MBIM_SERVICE_USSD:
+        return cid_ussd_config[cid - 1].query;
+    case MBIM_SERVICE_PHONEBOOK:
+        return cid_phonebook_config[cid - 1].query;
+    case MBIM_SERVICE_STK:
+        return cid_stk_config[cid - 1].query;
+    case MBIM_SERVICE_AUTH:
+        return cid_auth_config[cid - 1].query;
+    case MBIM_SERVICE_DSS:
+        return cid_dss_config[cid - 1].query;
+    default:
+        g_assert_not_reached ();
+        return FALSE;
+    }
+}
+
+/**
+ * mbim_cid_can_notify:
+ * @service: a #MbimService.
+ * @cid: a command ID.
+ *
+ * Checks whether the given command allows notifying.
+ *
+ * Returns: %TRUE if the command allows notifying, %FALSE otherwise.
+ */
+gboolean
+mbim_cid_can_notify (MbimService service,
+                     guint       cid)
+{
+    /* CID = 0 is never a valid command */
+    g_return_val_if_fail (cid > 0, FALSE);
+    /* Known service required */
+    g_return_val_if_fail (service > MBIM_SERVICE_INVALID, FALSE);
+    g_return_val_if_fail (service <= MBIM_SERVICE_DSS, FALSE);
+
+    switch (service) {
+    case MBIM_SERVICE_BASIC_CONNECT:
+        return cid_basic_connect_config[cid - 1].notify;
+    case MBIM_SERVICE_SMS:
+        return cid_sms_config[cid - 1].notify;
+    case MBIM_SERVICE_USSD:
+        return cid_ussd_config[cid - 1].notify;
+    case MBIM_SERVICE_PHONEBOOK:
+        return cid_phonebook_config[cid - 1].notify;
+    case MBIM_SERVICE_STK:
+        return cid_stk_config[cid - 1].notify;
+    case MBIM_SERVICE_AUTH:
+        return cid_auth_config[cid - 1].notify;
+    case MBIM_SERVICE_DSS:
+        return cid_dss_config[cid - 1].notify;
+    default:
+        g_assert_not_reached ();
+        return FALSE;
+    }
+}
+
+/**
+ * mbim_cid_get_printable:
+ * @service: a #MbimService.
+ * @cid: a command ID.
+ *
+ * Gets a printable string for the command specified by the @service and the
+ * @cid.
+ *
+ * Returns: (transfer none): a constant string.
+ */
+const gchar *
+mbim_cid_get_printable (MbimService service,
+                        guint       cid)
+{
+    /* CID = 0 is never a valid command */
+    g_return_val_if_fail (cid > 0, NULL);
+    /* Known service required */
+    g_return_val_if_fail (service > MBIM_SERVICE_INVALID, NULL);
+    g_return_val_if_fail (service <= MBIM_SERVICE_DSS, NULL);
+
+    switch (service) {
+    case MBIM_SERVICE_BASIC_CONNECT:
+        return mbim_cid_basic_connect_get_string (cid);
+    case MBIM_SERVICE_SMS:
+        return mbim_cid_sms_get_string (cid);
+    case MBIM_SERVICE_USSD:
+        return mbim_cid_ussd_get_string (cid);
+    case MBIM_SERVICE_PHONEBOOK:
+        return mbim_cid_phonebook_get_string (cid);
+    case MBIM_SERVICE_STK:
+        return mbim_cid_stk_get_string (cid);
+    case MBIM_SERVICE_AUTH:
+        return mbim_cid_auth_get_string (cid);
+    case MBIM_SERVICE_DSS:
+        return mbim_cid_dss_get_string (cid);
+    default:
+        g_assert_not_reached ();
+        return FALSE;
+    }
+}
diff --git a/libmbim-glib/mbim-cid.h b/libmbim-glib/mbim-cid.h
new file mode 100644
index 0000000..031baa0
--- /dev/null
+++ b/libmbim-glib/mbim-cid.h
@@ -0,0 +1,199 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#ifndef _LIBMBIM_GLIB_MBIM_CID_H_
+#define _LIBMBIM_GLIB_MBIM_CID_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 "mbim-uuid.h"
+
+G_BEGIN_DECLS
+
+/**
+ * MbimCidBasicConnect:
+ * @MBIM_CID_BASIC_CONNECT_UNKNOWN: Unknown command.
+ * @MBIM_CID_BASIC_CONNECT_DEVICE_CAPS: Device capabilities.
+ * @MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS: Subscriber ready status.
+ * @MBIM_CID_BASIC_CONNECT_RADIO_STATE: Radio state.
+ * @MBIM_CID_BASIC_CONNECT_PIN: PIN.
+ * @MBIM_CID_BASIC_CONNECT_PIN_LIST: PIN list.
+ * @MBIM_CID_BASIC_CONNECT_HOME_PROVIDER: Home provider.
+ * @MBIM_CID_BASIC_CONNECT_PREFERRED_PROVIDERS: Preferred providers.
+ * @MBIM_CID_BASIC_CONNECT_VISIBLE_PROVIDERS: Visible providers.
+ * @MBIM_CID_BASIC_CONNECT_REGISTER_STATE: Register state.
+ * @MBIM_CID_BASIC_CONNECT_PACKET_SERVICE: Packet service.
+ * @MBIM_CID_BASIC_CONNECT_SIGNAL_STATE: Signal state.
+ * @MBIM_CID_BASIC_CONNECT_CONNECT: Connect.
+ * @MBIM_CID_BASIC_CONNECT_PROVISIONED_CONTEXTS: Provisioned contexts.
+ * @MBIM_CID_BASIC_CONNECT_SERVICE_ACTIVATION: Service activation.
+ * @MBIM_CID_BASIC_CONNECT_IP_CONFIGURATION: IP configuration.
+ * @MBIM_CID_BASIC_CONNECT_DEVICE_SERVICES: Device services.
+ * @MBIM_CID_BASIC_CONNECT_DEVICE_SERVICE_SUBSCRIBER_LIST: Device service subscriber list.
+ * @MBIM_CID_BASIC_CONNECT_PACKET_STATISTICS: Packet statistics.
+ * @MBIM_CID_BASIC_CONNECT_NETWORK_IDLE_HINT: Network idle hint.
+ * @MBIM_CID_BASIC_CONNECT_EMERGENCY_MODE: Emergency mode.
+ * @MBIM_CID_BASIC_CONNECT_IP_PACKET_FILTERS: IP packet filters.
+ * @MBIM_CID_BASIC_CONNECT_MULTICARRIER_PROVIDERS: Multicarrier providers.
+ *
+ * MBIM commands in the %MBIM_SERVICE_BASIC_CONNECT service.
+ */
+typedef enum {
+    MBIM_CID_BASIC_CONNECT_UNKNOWN                        = 0,
+    MBIM_CID_BASIC_CONNECT_DEVICE_CAPS                    = 1,
+    MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS        = 2,
+    MBIM_CID_BASIC_CONNECT_RADIO_STATE                    = 3,
+    MBIM_CID_BASIC_CONNECT_PIN                            = 4,
+    MBIM_CID_BASIC_CONNECT_PIN_LIST                       = 5,
+    MBIM_CID_BASIC_CONNECT_HOME_PROVIDER                  = 6,
+    MBIM_CID_BASIC_CONNECT_PREFERRED_PROVIDERS            = 7,
+    MBIM_CID_BASIC_CONNECT_VISIBLE_PROVIDERS              = 8,
+    MBIM_CID_BASIC_CONNECT_REGISTER_STATE                 = 9,
+    MBIM_CID_BASIC_CONNECT_PACKET_SERVICE                 = 10,
+    MBIM_CID_BASIC_CONNECT_SIGNAL_STATE                   = 11,
+    MBIM_CID_BASIC_CONNECT_CONNECT                        = 12,
+    MBIM_CID_BASIC_CONNECT_PROVISIONED_CONTEXTS           = 13,
+    MBIM_CID_BASIC_CONNECT_SERVICE_ACTIVATION             = 14,
+    MBIM_CID_BASIC_CONNECT_IP_CONFIGURATION               = 15,
+    MBIM_CID_BASIC_CONNECT_DEVICE_SERVICES                = 16,
+    /* 17, 18 reserved */
+    MBIM_CID_BASIC_CONNECT_DEVICE_SERVICE_SUBSCRIBER_LIST = 19,
+    MBIM_CID_BASIC_CONNECT_PACKET_STATISTICS              = 20,
+    MBIM_CID_BASIC_CONNECT_NETWORK_IDLE_HINT              = 21,
+    MBIM_CID_BASIC_CONNECT_EMERGENCY_MODE                 = 22,
+    MBIM_CID_BASIC_CONNECT_IP_PACKET_FILTERS              = 23,
+    MBIM_CID_BASIC_CONNECT_MULTICARRIER_PROVIDERS         = 24,
+} MbimCidBasicConnect;
+
+/**
+ * MbimCidSms:
+ * @MBIM_CID_SMS_UNKNOWN: Unknown command.
+ * @MBIM_CID_SMS_CONFIGURATION: SMS configuration.
+ * @MBIM_CID_SMS_READ: Read.
+ * @MBIM_CID_SMS_SEND: Send.
+ * @MBIM_CID_SMS_DELETE: Delete.
+ * @MBIM_CID_SMS_MESSAGE_STORE_STATUS: Store message status.
+ *
+ * MBIM commands in the %MBIM_SERVICE_SMS service.
+ */
+typedef enum {
+    MBIM_CID_SMS_UNKNOWN              = 0,
+    MBIM_CID_SMS_CONFIGURATION        = 1,
+    MBIM_CID_SMS_READ                 = 2,
+    MBIM_CID_SMS_SEND                 = 3,
+    MBIM_CID_SMS_DELETE               = 4,
+    MBIM_CID_SMS_MESSAGE_STORE_STATUS = 5,
+} MbimCidSms;
+
+/**
+ * MbimCidUssd:
+ * @MBIM_CID_USSD_UNKNOWN: Unknown command.
+ * @MBIM_CID_USSD: USSD operation.
+ *
+ * MBIM commands in the %MBIM_SERVICE_USSD service.
+ */
+typedef enum {
+    MBIM_CID_USSD_UNKNOWN = 0,
+    MBIM_CID_USSD         = 1,
+} MbimCidUssd;
+
+/**
+ * MbimCidPhonebook:
+ * @MBIM_CID_PHONEBOOK_UNKNOWN: Unknown command.
+ * @MBIM_CID_PHONEBOOK_CONFIGURATION: Configuration.
+ * @MBIM_CID_PHONEBOOK_READ: Read.
+ * @MBIM_CID_PHONEBOOK_DELETE: Delete.
+ * @MBIM_CID_PHONEBOOK_WRITE: Write.
+ *
+ * MBIM commands in the %MBIM_SERVICE_PHONEBOOK service.
+ */
+typedef enum {
+    MBIM_CID_PHONEBOOK_UNKNOWN       = 0,
+    MBIM_CID_PHONEBOOK_CONFIGURATION = 1,
+    MBIM_CID_PHONEBOOK_READ          = 2,
+    MBIM_CID_PHONEBOOK_DELETE        = 3,
+    MBIM_CID_PHONEBOOK_WRITE         = 4,
+} MbimCidPhonebook;
+
+/**
+ * MbimCidStk:
+ * @MBIM_CID_STK_UNKNOWN: Unknown command.
+ * @MBIM_CID_STK_PAC: PAC.
+ * @MBIM_CID_STK_TERMINAL_RESPONSE: Terminal response.
+ * @MBIM_CID_STK_ENVELOPE: Envelope.
+ *
+ * MBIM commands in the %MBIM_SERVICE_STK service.
+ */
+typedef enum {
+    MBIM_CID_STK_UNKNOWN           = 0,
+    MBIM_CID_STK_PAC               = 1,
+    MBIM_CID_STK_TERMINAL_RESPONSE = 2,
+    MBIM_CID_STK_ENVELOPE          = 3,
+} MbimCidStk;
+
+/**
+ * MbimCidAuth:
+ * @MBIM_CID_AUTH_UNKNOWN: Unknow command
+ * @MBIM_CID_AUTH_AKA: AKA.
+ * @MBIM_CID_AUTH_AKAP: AKAP.
+ * @MBIM_CID_AUTH_SIM: SIM.
+ *
+ * MBIM commands in the %MBIM_SERVICE_AUTH service.
+ */
+typedef enum {
+    MBIM_CID_AUTH_UNKNOWN = 0,
+    MBIM_CID_AUTH_AKA     = 1,
+    MBIM_CID_AUTH_AKAP    = 2,
+    MBIM_CID_AUTH_SIM     = 3,
+} MbimCidAuth;
+
+/**
+ * MbimCidDss:
+ * @MBIM_CID_DSS_UNKNOWN: Unknown command.
+ * @MBIM_CID_DSS_CONNECT: Connect.
+ *
+ * MBIM commands in the %MBIM_SERVICE_DSS service.
+ */
+typedef enum {
+    MBIM_CID_DSS_UNKNOWN = 0,
+    MBIM_CID_DSS_CONNECT = 1
+} MbimCidDss;
+
+/* Command helpers */
+
+gboolean     mbim_cid_can_set       (MbimService service,
+                                     guint       cid);
+gboolean     mbim_cid_can_query     (MbimService service,
+                                     guint       cid);
+gboolean     mbim_cid_can_notify    (MbimService service,
+                                     guint       cid);
+const gchar *mbim_cid_get_printable (MbimService service,
+                                     guint       cid);
+
+G_END_DECLS
+
+#endif /* _LIBMBIM_GLIB_MBIM_CID_H_ */
diff --git a/libmbim-glib/mbim-device.c b/libmbim-glib/mbim-device.c
new file mode 100644
index 0000000..d8c6f81
--- /dev/null
+++ b/libmbim-glib/mbim-device.c
@@ -0,0 +1,1836 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@lanedo.com>
+ *
+ * Implementation based on the 'QmiDevice' GObject from libqmi-glib.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <gio/gio.h>
+#include <gudev/gudev.h>
+#include <sys/ioctl.h>
+#define IOCTL_WDM_MAX_COMMAND _IOR('H', 0xA0, guint16)
+
+#include "mbim-utils.h"
+#include "mbim-device.h"
+#include "mbim-message.h"
+#include "mbim-message-private.h"
+#include "mbim-error-types.h"
+
+/**
+ * SECTION:mbim-device
+ * @title: MbimDevice
+ * @short_description: Generic MBIM device handling routines
+ *
+ * #MbimDevice is a generic type in charge of controlling the access to the
+ * managed MBIM port.
+ *
+ * A #MbimDevice can only handle one single MBIM port.
+ */
+
+static void async_initable_iface_init (GAsyncInitableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MbimDevice, mbim_device, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init))
+
+enum {
+    PROP_0,
+    PROP_FILE,
+    PROP_TRANSACTION_ID,
+    PROP_IN_SESSION,
+    PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+enum {
+    SIGNAL_INDICATE_STATUS,
+    SIGNAL_ERROR,
+    SIGNAL_LAST
+};
+
+static guint signals[SIGNAL_LAST] = { 0 };
+
+typedef enum {
+    TRANSACTION_TYPE_HOST  = 0,
+    TRANSACTION_TYPE_MODEM = 1,
+    TRANSACTION_TYPE_LAST  = 2
+} TransactionType;
+
+struct _MbimDevicePrivate {
+    /* File */
+    GFile *file;
+    gchar *path;
+    gchar *path_display;
+
+    /* I/O channel, set when the file is open */
+    GIOChannel *iochannel;
+    guint watch_id;
+    GByteArray *response;
+
+    /* HT to keep track of ongoing host/function transactions
+     *  Host transactions:  created by us
+     *  Modem transactions: modem-created indications with multiple fragments
+     */
+    GHashTable *transactions[TRANSACTION_TYPE_LAST];
+
+    /* Transaction ID in the device */
+    guint32 transaction_id;
+
+    /* Flag to specify whether we're in a session */
+    gboolean in_session;
+
+    /* message size */
+    guint16 max_control_transfer;
+};
+
+#define MAX_CONTROL_TRANSFER          4096
+#define MAX_TIME_BETWEEN_FRAGMENTS_MS 1250
+
+static void device_report_error (MbimDevice   *self,
+                                 guint32       transaction_id,
+                                 const GError *error);
+
+/*****************************************************************************/
+/* Message transactions (private) */
+
+typedef struct {
+    MbimDevice *self;
+    guint32 transaction_id;
+    TransactionType type;
+} TransactionWaitContext;
+
+typedef struct {
+    MbimMessage *fragments;
+    guint32 transaction_id;
+    GSimpleAsyncResult *result;
+    guint timeout_id;
+    GCancellable *cancellable;
+    gulong cancellable_id;
+    TransactionWaitContext *wait_ctx;
+} Transaction;
+
+static Transaction *
+transaction_new (MbimDevice          *self,
+                 guint32              transaction_id,
+                 GCancellable        *cancellable,
+                 GAsyncReadyCallback  callback,
+                 gpointer             user_data)
+{
+    Transaction *tr;
+
+    tr = g_slice_new0 (Transaction);
+    tr->transaction_id = transaction_id;
+    tr->result = g_simple_async_result_new (G_OBJECT (self),
+                                            callback,
+                                            user_data,
+                                            transaction_new);
+    if (cancellable)
+        tr->cancellable = g_object_ref (cancellable);
+
+    return tr;
+}
+
+static void
+transaction_complete_and_free (Transaction  *tr,
+                               const GError *error)
+{
+    if (tr->timeout_id)
+        g_source_remove (tr->timeout_id);
+
+    if (tr->cancellable) {
+        if (tr->cancellable_id)
+            g_cancellable_disconnect (tr->cancellable, tr->cancellable_id);
+        g_object_unref (tr->cancellable);
+    }
+
+    if (tr->wait_ctx)
+        g_slice_free (TransactionWaitContext, tr->wait_ctx);
+
+    if (error) {
+        g_simple_async_result_set_from_error (tr->result, error);
+        if (tr->fragments)
+            mbim_message_unref (tr->fragments);
+    } else {
+        g_assert (tr->fragments != NULL);
+        g_simple_async_result_set_op_res_gpointer (tr->result,
+                                                   tr->fragments,
+                                                   (GDestroyNotify) mbim_message_unref);
+    }
+
+    g_simple_async_result_complete_in_idle (tr->result);
+    g_object_unref (tr->result);
+    g_slice_free (Transaction, tr);
+}
+
+static Transaction *
+device_release_transaction (MbimDevice      *self,
+                            TransactionType  type,
+                            guint32          transaction_id)
+{
+    Transaction *tr = NULL;
+
+    if (self->priv->transactions[type]) {
+        tr = g_hash_table_lookup (self->priv->transactions[type], GUINT_TO_POINTER (transaction_id));
+        if (tr)
+            /* If found, remove it from the HT */
+            g_hash_table_remove (self->priv->transactions[type], GUINT_TO_POINTER (transaction_id));
+    }
+
+    return tr;
+}
+
+static gboolean
+transaction_timed_out (TransactionWaitContext *ctx)
+{
+    Transaction *tr;
+    GError *error = NULL;
+
+    tr = device_release_transaction (ctx->self, ctx->type, ctx->transaction_id);
+    tr->timeout_id = 0;
+
+    /* If no fragment was received, complete transaction with a timeout error */
+    if (!tr->fragments)
+        error = g_error_new (MBIM_CORE_ERROR,
+                             MBIM_CORE_ERROR_TIMEOUT,
+                             "Transaction timed out");
+    else {
+        /* Fragment timeout... */
+        error = g_error_new (MBIM_PROTOCOL_ERROR,
+                             MBIM_PROTOCOL_ERROR_TIMEOUT_FRAGMENT,
+                             "Fragment timed out");
+
+        /* Also notify to the modem */
+        device_report_error (ctx->self,
+                             tr->transaction_id,
+                             error);
+    }
+
+    transaction_complete_and_free (tr, error);
+    g_error_free (error);
+
+    return FALSE;
+}
+
+static void
+transaction_cancelled (GCancellable           *cancellable,
+                       TransactionWaitContext *ctx)
+{
+    Transaction *tr;
+    GError *error = NULL;
+
+    tr = device_release_transaction (ctx->self, ctx->type, ctx->transaction_id);
+    tr->cancellable_id = 0;
+
+    /* Complete transaction with an abort error */
+    error = g_error_new (MBIM_CORE_ERROR,
+                         MBIM_CORE_ERROR_ABORTED,
+                         "Transaction aborted");
+    transaction_complete_and_free (tr, error);
+    g_error_free (error);
+}
+
+static gboolean
+device_store_transaction (MbimDevice       *self,
+                          TransactionType   type,
+                          Transaction      *tr,
+                          guint             timeout_ms,
+                          GError          **error)
+{
+    if (G_UNLIKELY (!self->priv->transactions[type]))
+        self->priv->transactions[type] = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+    tr->wait_ctx = g_slice_new (TransactionWaitContext);
+    tr->wait_ctx->self = self;
+     /* valid as long as the transaction is in the HT */
+    tr->wait_ctx->transaction_id = tr->transaction_id;
+    tr->wait_ctx->type = type;
+
+    tr->timeout_id = g_timeout_add (timeout_ms,
+                                    (GSourceFunc)transaction_timed_out,
+                                    tr->wait_ctx);
+
+    if (tr->cancellable) {
+        tr->cancellable_id = g_cancellable_connect (tr->cancellable,
+                                                    (GCallback)transaction_cancelled,
+                                                    tr->wait_ctx,
+                                                    NULL);
+        if (!tr->cancellable_id) {
+            g_set_error_literal (error,
+                                 MBIM_CORE_ERROR,
+                                 MBIM_CORE_ERROR_ABORTED,
+                                 "Request is already cancelled");
+            return FALSE;
+        }
+    }
+
+    /* Keep in the HT */
+    g_hash_table_insert (self->priv->transactions[type], GUINT_TO_POINTER (tr->transaction_id), tr);
+
+    return TRUE;
+}
+
+static Transaction *
+device_match_transaction (MbimDevice        *self,
+                          TransactionType    type,
+                          const MbimMessage *message)
+{
+    /* msg can be either the original message or the response */
+    return device_release_transaction (self, type, mbim_message_get_transaction_id (message));
+}
+
+/*****************************************************************************/
+
+/**
+ * mbim_device_get_file:
+ * @self: a #MbimDevice.
+ *
+ * Get the #GFile associated with this #MbimDevice.
+ *
+ * Returns: a #GFile that must be freed with g_object_unref().
+ */
+GFile *
+mbim_device_get_file (MbimDevice *self)
+{
+    GFile *file = NULL;
+
+    g_return_val_if_fail (MBIM_IS_DEVICE (self), NULL);
+
+    g_object_get (G_OBJECT (self),
+                  MBIM_DEVICE_FILE, &file,
+                  NULL);
+    return file;
+}
+
+/**
+ * mbim_device_peek_file:
+ * @self: a #MbimDevice.
+ *
+ * Get the #GFile associated with this #MbimDevice, without increasing the reference count
+ * on the returned object.
+ *
+ * Returns: a #GFile. Do not free the returned object, it is owned by @self.
+ */
+GFile *
+mbim_device_peek_file (MbimDevice *self)
+{
+    g_return_val_if_fail (MBIM_IS_DEVICE (self), NULL);
+
+    return self->priv->file;
+}
+
+/**
+ * mbim_device_get_path:
+ * @self: a #MbimDevice.
+ *
+ * Get the system path of the underlying MBIM device.
+ *
+ * Returns: the system path of the device.
+ */
+const gchar *
+mbim_device_get_path (MbimDevice *self)
+{
+    g_return_val_if_fail (MBIM_IS_DEVICE (self), NULL);
+
+    return self->priv->path;
+}
+
+/**
+ * mbim_device_get_path_display:
+ * @self: a #MbimDevice.
+ *
+ * Get the system path of the underlying MBIM device in UTF-8.
+ *
+ * Returns: UTF-8 encoded system path of the device.
+ */
+const gchar *
+mbim_device_get_path_display (MbimDevice *self)
+{
+    g_return_val_if_fail (MBIM_IS_DEVICE (self), NULL);
+
+    return self->priv->path_display;
+}
+
+/**
+ * mbim_device_is_open:
+ * @self: a #MbimDevice.
+ *
+ * Checks whether the #MbimDevice is open for I/O.
+ *
+ * Returns: %TRUE if @self is open, %FALSE otherwise.
+ */
+gboolean
+mbim_device_is_open (MbimDevice *self)
+{
+    g_return_val_if_fail (MBIM_IS_DEVICE (self), FALSE);
+
+    return !!self->priv->iochannel;
+}
+
+/*****************************************************************************/
+/* Open device */
+
+static void
+indication_ready (MbimDevice   *self,
+                  GAsyncResult *res)
+{
+    GError *error = NULL;
+    MbimMessage *indication;
+
+    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), &error)) {
+        g_debug ("[%s] Error processing indication message: %s",
+                 self->priv->path_display,
+                 error->message);
+        g_error_free (error);
+        return;
+    }
+
+    indication = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+    g_signal_emit (self, signals[SIGNAL_INDICATE_STATUS], 0, indication);
+}
+
+static void
+process_message (MbimDevice  *self,
+                 const MbimMessage *message)
+{
+    if (mbim_utils_get_traces_enabled ()) {
+        gchar *printable;
+        gboolean is_partial_fragment;
+
+        is_partial_fragment = (_mbim_message_is_fragment (message) &&
+                               _mbim_message_fragment_get_total (message) > 1);
+
+        printable = __mbim_utils_str_hex (((GByteArray *)message)->data,
+                                          ((GByteArray *)message)->len,
+                                          ':');
+        g_debug ("[%s] Received message...%s\n"
+                 ">>>>>> RAW:\n"
+                 ">>>>>>   length = %u\n"
+                 ">>>>>>   data   = %s\n",
+                 self->priv->path_display,
+                 is_partial_fragment ? " (partial fragment)" : "",
+                 ((GByteArray *)message)->len,
+                 printable);
+        g_free (printable);
+
+        if (is_partial_fragment) {
+            printable = mbim_message_get_printable (message, ">>>>>> ", TRUE);
+            g_debug ("[%s] Received message fragment (translated)...\n%s",
+                     self->priv->path_display,
+                     printable);
+            g_free (printable);
+        }
+    }
+
+    switch (MBIM_MESSAGE_GET_MESSAGE_TYPE (message)) {
+    case MBIM_MESSAGE_TYPE_OPEN_DONE:
+    case MBIM_MESSAGE_TYPE_CLOSE_DONE:
+    case MBIM_MESSAGE_TYPE_COMMAND_DONE:
+    case MBIM_MESSAGE_TYPE_INDICATE_STATUS: {
+        GError *error = NULL;
+        Transaction *tr;
+
+        if (MBIM_MESSAGE_GET_MESSAGE_TYPE (message) == MBIM_MESSAGE_TYPE_INDICATE_STATUS) {
+            /* Grab transaction */
+            tr = device_match_transaction (self, TRANSACTION_TYPE_MODEM, message);
+            if (!tr)
+                /* Create new transaction for the indication */
+                tr = transaction_new (self,
+                                      mbim_message_get_transaction_id (message),
+                                      NULL, /* no cancellable */
+                                      (GAsyncReadyCallback)indication_ready,
+                                      NULL);
+        } else {
+            /* Grab transaction */
+            tr = device_match_transaction (self, TRANSACTION_TYPE_HOST, message);
+            if (!tr) {
+                g_debug ("[%s] No transaction matched in received message",
+                         self->priv->path_display);
+                return;
+            }
+
+            /* If the message doesn't have fragments, we're done */
+            if (!_mbim_message_is_fragment (message)) {
+                g_assert (tr->fragments == NULL);
+                tr->fragments = mbim_message_dup (message);
+                transaction_complete_and_free (tr, NULL);
+                return;
+            }
+        }
+
+        /* More than one fragment expected; is this the first one? */
+        if (!tr->fragments)
+            tr->fragments = _mbim_message_fragment_collector_init (message, &error);
+        else
+            _mbim_message_fragment_collector_add (tr->fragments, message, &error);
+
+        if (error) {
+            device_report_error (self,
+                                 tr->transaction_id,
+                                 error);
+            transaction_complete_and_free (tr, error);
+            g_error_free (error);
+            return;
+        }
+
+        /* Did we get all needed fragments? */
+        if (_mbim_message_fragment_collector_complete (tr->fragments)) {
+            /* Now, translate the whole message */
+            if (mbim_utils_get_traces_enabled ()) {
+                gchar *printable;
+
+                printable = mbim_message_get_printable (tr->fragments, ">>>>>> ", FALSE);
+                g_debug ("[%s] Received message (translated)...\n%s",
+                         self->priv->path_display,
+                         printable);
+                g_free (printable);
+            }
+
+            transaction_complete_and_free (tr, NULL);
+            return;
+        }
+
+        /* Need more fragments, store transaction */
+        g_assert (device_store_transaction (self,
+                                            TRANSACTION_TYPE_HOST,
+                                            tr,
+                                            MAX_TIME_BETWEEN_FRAGMENTS_MS,
+                                            NULL));
+        return;
+    }
+
+    case MBIM_MESSAGE_TYPE_FUNCTION_ERROR: {
+        GError *error_indication;
+
+        if (mbim_utils_get_traces_enabled ()) {
+            gchar *printable;
+
+            printable = mbim_message_get_printable (message, ">>>>>> ", FALSE);
+            g_debug ("[%s] Received message (translated)...\n%s",
+                     self->priv->path_display,
+                     printable);
+            g_free (printable);
+        }
+
+        error_indication = mbim_message_error_get_error (message);
+        g_signal_emit (self, signals[SIGNAL_ERROR], 0, error_indication);
+        g_error_free (error_indication);
+        return;
+    }
+
+    case MBIM_MESSAGE_TYPE_INVALID:
+    case MBIM_MESSAGE_TYPE_OPEN:
+    case MBIM_MESSAGE_TYPE_CLOSE:
+    case MBIM_MESSAGE_TYPE_COMMAND:
+    case MBIM_MESSAGE_TYPE_HOST_ERROR:
+        /* Shouldn't expect host-generated messages as replies */
+        g_warning ("[%s] Host-generated message received: ignoring",
+                   self->priv->path_display);
+        return;
+    }
+}
+
+static void
+parse_response (MbimDevice *self)
+{
+    do {
+        const MbimMessage *message;
+        guint32 in_length;
+
+        /* If not even the MBIM header available, just return */
+        if (self->priv->response->len < 12)
+            return;
+
+        message = (const MbimMessage *)self->priv->response;
+
+        /* No full message yet */
+        in_length = mbim_message_get_message_length (message);
+
+        if (self->priv->response->len < in_length)
+            return;
+
+        /* Play with the received message */
+        process_message (self, message);
+
+        /* Remove message from buffer */
+        g_byte_array_remove_range (self->priv->response, 0, in_length);
+    } while (self->priv->response->len > 0);
+}
+
+static gboolean
+data_available (GIOChannel *source,
+                GIOCondition condition,
+                MbimDevice *self)
+{
+    gsize bytes_read;
+    GIOStatus status;
+    gchar buffer[MAX_CONTROL_TRANSFER + 1];
+
+    if (condition & G_IO_HUP) {
+        g_debug ("[%s] unexpected port hangup!",
+                 self->priv->path_display);
+
+        if (self->priv->response &&
+            self->priv->response->len)
+            g_byte_array_remove_range (self->priv->response, 0, self->priv->response->len);
+
+        mbim_device_close_force (self, NULL);
+        return FALSE;
+    }
+
+    if (condition & G_IO_ERR) {
+        if (self->priv->response &&
+            self->priv->response->len)
+            g_byte_array_remove_range (self->priv->response, 0, self->priv->response->len);
+        return TRUE;
+    }
+
+    /* If not ready yet, prepare the response with default initial size. */
+    if (G_UNLIKELY (!self->priv->response))
+        self->priv->response = g_byte_array_sized_new (500);
+
+    do {
+        GError *error = NULL;
+
+        status = g_io_channel_read_chars (source,
+                                          buffer,
+                                          self->priv->max_control_transfer,
+                                          &bytes_read,
+                                          &error);
+        if (status == G_IO_STATUS_ERROR) {
+            if (error) {
+                g_warning ("[%s] error reading from the IOChannel: '%s'",
+                           self->priv->path_display,
+                           error->message);
+                g_error_free (error);
+            }
+
+            /* Port is closed; we're done */
+            if (self->priv->watch_id == 0)
+                break;
+        }
+
+        /* If no bytes read, just let g_io_channel wait for more data */
+        if (bytes_read == 0)
+            break;
+
+        if (bytes_read > 0)
+            g_byte_array_append (self->priv->response, (const guint8 *)buffer, bytes_read);
+
+        /* Try to parse what we already got */
+        parse_response (self);
+
+        /* And keep on if we were told to keep on */
+    } while (bytes_read == self->priv->max_control_transfer || status == G_IO_STATUS_AGAIN);
+
+    return TRUE;
+}
+
+/* "MBIM Control Model Functional Descriptor" */
+struct usb_cdc_mbim_desc {
+    guint8  bLength;
+    guint8  bDescriptorType;
+    guint8  bDescriptorSubType;
+    guint16 bcdMBIMVersion;
+    guint16 wMaxControlMessage;
+    guint8  bNumberFilters;
+    guint8  bMaxFilterSize;
+    guint16 wMaxSegmentSize;
+    guint8  bmNetworkCapabilities;
+} __attribute__ ((packed));
+
+static guint16
+read_max_control_transfer (MbimDevice *self)
+{
+    static const guint8 mbim_signature[4] = { 0x0c, 0x24, 0x1b, 0x00 };
+    guint16 max = MAX_CONTROL_TRANSFER;
+    GUdevClient *client;
+    GUdevDevice *device = NULL;
+    GUdevDevice *parent_device = NULL;
+    GUdevDevice *grandparent_device = NULL;
+    gchar *descriptors_path = NULL;
+    gchar *device_basename = NULL;
+    GError *error = NULL;
+    gchar *contents = NULL;
+    gsize length = 0;
+    guint i;
+
+    client = g_udev_client_new (NULL);
+    if (!G_UDEV_IS_CLIENT (client)) {
+        g_warning ("[%s] Couldn't get udev client",
+                   self->priv->path_display);
+        goto out;
+    }
+
+    /* We need to get the sysfs of the cdc-wdm's grandfather:
+     *
+     *   * Device's sysfs path is like:
+     *      /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.5/2-1.5:2.0/usbmisc/cdc-wdm0
+     *   * Parent's sysfs path is like:
+     *      /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.5/2-1.5:2.0
+     *   * Grandparent's sysfs path is like:
+     *      /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.5
+     *
+     *   Which is the one with the descriptors file.
+     */
+
+    device_basename = g_path_get_basename (self->priv->path);
+    device = g_udev_client_query_by_subsystem_and_name (client, "usb", device_basename);
+    if (!device) {
+        device = g_udev_client_query_by_subsystem_and_name (client, "usbmisc", device_basename);
+        if (!device) {
+            g_warning ("[%s] Couldn't find udev device",
+                       self->priv->path_display);
+            goto out;
+        }
+    }
+
+    parent_device = g_udev_device_get_parent (device);
+    if (!parent_device) {
+        g_warning ("[%s] Couldn't find parent udev device",
+                   self->priv->path_display);
+        goto out;
+    }
+
+    grandparent_device = g_udev_device_get_parent (parent_device);
+    if (!grandparent_device) {
+        g_warning ("[%s] Couldn't find grandparent udev device",
+                   self->priv->path_display);
+        goto out;
+    }
+
+    descriptors_path = g_build_path (G_DIR_SEPARATOR_S,
+                                     g_udev_device_get_sysfs_path (grandparent_device),
+                                     "descriptors",
+                                     NULL);
+    if (!g_file_get_contents (descriptors_path,
+                              &contents,
+                              &length,
+                              &error)) {
+        g_warning ("[%s] Couldn't read descriptors file: %s",
+                   self->priv->path_display,
+                   error->message);
+        g_error_free (error);
+        goto out;
+    }
+
+    i = 0;
+    while (i <= (length - sizeof (struct usb_cdc_mbim_desc))) {
+        /* Try to match the MBIM descriptor signature */
+        if ((memcmp (&contents[i], mbim_signature, sizeof (mbim_signature)) == 0)) {
+            /* Found! */
+            max = GUINT16_FROM_LE (((struct usb_cdc_mbim_desc *)&contents[i])->wMaxControlMessage);
+            g_debug ("[%s] Read max control message size from descriptors file: %" G_GUINT16_FORMAT,
+                     self->priv->path_display,
+                     max);
+            goto out;
+        }
+
+        /* The first byte of the descriptor info is the length; so keep on
+         * skipping descriptors until we match the MBIM one */
+        i += contents[i];
+    }
+
+    g_warning ("[%s] Couldn't find MBIM signature in descriptors file",
+               self->priv->path_display);
+
+out:
+    g_free (contents);
+    g_free (device_basename);
+    g_free (descriptors_path);
+    if (parent_device)
+        g_object_unref (parent_device);
+    if (grandparent_device)
+        g_object_unref (grandparent_device);
+    if (device)
+        g_object_unref (device);
+    if (client)
+        g_object_unref (client);
+
+    return max;
+}
+
+static gboolean
+create_iochannel (MbimDevice *self,
+                  GError **error)
+{
+    GError *inner_error = NULL;
+    gint fd;
+    guint16 max;
+
+    if (self->priv->iochannel) {
+        g_set_error_literal (error,
+                             MBIM_CORE_ERROR,
+                             MBIM_CORE_ERROR_WRONG_STATE,
+                             "Already open");
+        return FALSE;
+    }
+
+    g_assert (self->priv->file);
+    g_assert (self->priv->path);
+
+    errno = 0;
+    fd = open (self->priv->path, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY);
+    if (fd < 0) {
+        g_set_error (error,
+                     MBIM_CORE_ERROR,
+                     MBIM_CORE_ERROR_FAILED,
+                     "Cannot open device file '%s': %s",
+                     self->priv->path_display,
+                     strerror (errno));
+        return FALSE;
+    }
+
+    /* Query message size */
+    if (ioctl (fd, IOCTL_WDM_MAX_COMMAND, &max) < 0) {
+        g_debug ("[%s] Couldn't query maximum message size: "
+                 "IOCTL_WDM_MAX_COMMAND failed: %s",
+                 self->priv->path_display,
+                 strerror (errno));
+        /* Fallback, try to read the descriptor file */
+        max = read_max_control_transfer (self);
+    } else {
+        g_debug ("[%s] Queried max control message size: %" G_GUINT16_FORMAT,
+                 self->priv->path_display,
+                 max);
+    }
+    self->priv->max_control_transfer = max;
+
+    /* Create new GIOChannel */
+    self->priv->iochannel = g_io_channel_unix_new (fd);
+
+    /* We don't want UTF-8 encoding, we're playing with raw binary data */
+    g_io_channel_set_encoding (self->priv->iochannel, NULL, NULL);
+
+    /* We don't want to get the channel buffered */
+    g_io_channel_set_buffered (self->priv->iochannel, FALSE);
+
+    /* Let the GIOChannel own the FD */
+    g_io_channel_set_close_on_unref (self->priv->iochannel, TRUE);
+
+    /* We don't want to get blocked while writing stuff */
+    if (!g_io_channel_set_flags (self->priv->iochannel,
+                                 G_IO_FLAG_NONBLOCK,
+                                 &inner_error)) {
+        g_prefix_error (&inner_error, "Cannot set non-blocking channel: ");
+        g_propagate_error (error, inner_error);
+        g_io_channel_shutdown (self->priv->iochannel, FALSE, NULL);
+        g_io_channel_unref (self->priv->iochannel);
+        self->priv->iochannel = NULL;
+        return FALSE;
+    }
+
+    self->priv->watch_id = g_io_add_watch (self->priv->iochannel,
+                                           G_IO_IN | G_IO_ERR | G_IO_HUP,
+                                           (GIOFunc)data_available,
+                                           self);
+
+    return !!self->priv->iochannel;
+}
+
+typedef struct {
+    MbimDevice *self;
+    GSimpleAsyncResult *result;
+    GCancellable *cancellable;
+    guint timeout;
+} DeviceOpenContext;
+
+static void
+device_open_context_complete_and_free (DeviceOpenContext *ctx)
+{
+    g_simple_async_result_complete_in_idle (ctx->result);
+    g_object_unref (ctx->result);
+    if (ctx->cancellable)
+        g_object_unref (ctx->cancellable);
+    g_object_unref (ctx->self);
+    g_slice_free (DeviceOpenContext, ctx);
+}
+
+/**
+ * mbim_device_open_finish:
+ * @self: a #MbimDevice.
+ * @res: a #GAsyncResult.
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an asynchronous open operation started with mbim_device_open().
+ *
+ * Returns: %TRUE if successful, %FALSE if @error is set.
+ */
+gboolean
+mbim_device_open_finish (MbimDevice   *self,
+                        GAsyncResult  *res,
+                        GError       **error)
+{
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void open_message (DeviceOpenContext *ctx);
+
+static void
+open_message_ready (MbimDevice        *self,
+                    GAsyncResult      *res,
+                    DeviceOpenContext *ctx)
+{
+    MbimMessage *response;
+    GError *error = NULL;
+
+    response = mbim_device_command_finish (self, res, &error);
+    if (!response) {
+        /* Check if we should be retrying */
+        if (g_error_matches (error, MBIM_CORE_ERROR, MBIM_CORE_ERROR_TIMEOUT)) {
+            /* The timeout will tell us how many retries we should do */
+            ctx->timeout--;
+            if (ctx->timeout) {
+                g_error_free (error);
+                open_message (ctx);
+                return;
+            }
+
+            /* No more seconds left in the timeout... return error */
+        }
+
+        g_simple_async_result_take_error (ctx->result, error);
+    } else if (!mbim_message_open_done_get_result (response, &error))
+        g_simple_async_result_take_error (ctx->result, error);
+    else
+        g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+
+    if (response)
+        mbim_message_unref (response);
+    device_open_context_complete_and_free (ctx);
+}
+
+static void
+open_message (DeviceOpenContext *ctx)
+{
+    MbimMessage *request;
+
+    /* Launch 'Open' command */
+    request = mbim_message_open_new (mbim_device_get_next_transaction_id (ctx->self),
+                                     ctx->self->priv->max_control_transfer);
+    mbim_device_command (ctx->self,
+                         request,
+                         1, /* 1s per retry */
+                         ctx->cancellable,
+                         (GAsyncReadyCallback)open_message_ready,
+                         ctx);
+    mbim_message_unref (request);
+}
+
+/**
+ * mbim_device_open:
+ * @self: a #MbimDevice.
+ * @timeout: maximum time, in seconds, to wait for the device to be opened.
+ * @cancellable: optional #GCancellable object, #NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the operation is finished.
+ * @user_data: the data to pass to callback function.
+ *
+ * Asynchronously opens a #MbimDevice for I/O.
+ *
+ * When the operation is finished @callback will be called. You can then call
+ * mbim_device_open_finish() to get the result of the operation.
+ */
+void
+mbim_device_open (MbimDevice          *self,
+                  guint                timeout,
+                  GCancellable        *cancellable,
+                  GAsyncReadyCallback  callback,
+                  gpointer             user_data)
+{
+    DeviceOpenContext *ctx;
+    GError *error = NULL;
+
+    g_return_if_fail (MBIM_IS_DEVICE (self));
+    g_return_if_fail (timeout > 0);
+
+    ctx = g_slice_new (DeviceOpenContext);
+    ctx->self = g_object_ref (self);
+    ctx->result = g_simple_async_result_new (G_OBJECT (self),
+                                             callback,
+                                             user_data,
+                                             mbim_device_open);
+    ctx->timeout = timeout;
+    ctx->cancellable = (cancellable ? g_object_ref (cancellable) : NULL);
+
+    if (!create_iochannel (self, &error)) {
+        g_prefix_error (&error,
+                        "Cannot open MBIM device: ");
+        g_simple_async_result_take_error (ctx->result, error);
+        device_open_context_complete_and_free (ctx);
+        return;
+    }
+
+    /* If the device is already in-session, avoid the open message */
+    if (self->priv->in_session) {
+        g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+        device_open_context_complete_and_free (ctx);
+        return;
+    }
+
+    open_message (ctx);
+}
+
+/*****************************************************************************/
+/* Close channel */
+
+static gboolean
+destroy_iochannel (MbimDevice  *self,
+                   GError     **error)
+{
+    GError *inner_error = NULL;
+
+    g_io_channel_shutdown (self->priv->iochannel, TRUE, &inner_error);
+
+    /* Failures when closing still make the device to get closed */
+    g_io_channel_unref (self->priv->iochannel);
+    self->priv->iochannel = NULL;
+
+    if (self->priv->watch_id) {
+        g_source_remove (self->priv->watch_id);
+        self->priv->watch_id = 0;
+    }
+
+    if (self->priv->response) {
+        g_byte_array_unref (self->priv->response);
+        self->priv->response = NULL;
+    }
+
+    if (inner_error) {
+        g_propagate_error (error, inner_error);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/**
+ * mbim_device_close_force:
+ * @self: a #MbimDevice.
+ * @error: Return location for error or %NULL.
+ *
+ * Forces the #MbimDevice to be closed.
+ *
+ * Returns: %TRUE if @self if no error happens, otherwise %FALSE and @error is set.
+ */
+gboolean
+mbim_device_close_force (MbimDevice *self,
+                         GError **error)
+{
+    g_return_val_if_fail (MBIM_IS_DEVICE (self), FALSE);
+
+    /* Already closed? */
+    if (!self->priv->iochannel)
+        return TRUE;
+
+    return destroy_iochannel (self, error);
+}
+
+typedef struct {
+    MbimDevice *self;
+    GSimpleAsyncResult *result;
+    GCancellable *cancellable;
+    guint timeout;
+} DeviceCloseContext;
+
+static void
+device_close_context_complete_and_free (DeviceCloseContext *ctx)
+{
+    g_simple_async_result_complete_in_idle (ctx->result);
+    g_object_unref (ctx->result);
+    if (ctx->cancellable)
+        g_object_unref (ctx->cancellable);
+    g_object_unref (ctx->self);
+    g_slice_free (DeviceCloseContext, ctx);
+}
+
+/**
+ * mbim_device_close_finish:
+ * @self: a #MbimDevice.
+ * @res: a #GAsyncResult.
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an asynchronous close operation started with mbim_device_close().
+ *
+ * Returns: %TRUE if successful, %FALSE if @error is set.
+ */
+gboolean
+mbim_device_close_finish (MbimDevice    *self,
+                          GAsyncResult  *res,
+                          GError       **error)
+{
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+close_message_ready (MbimDevice         *self,
+                     GAsyncResult       *res,
+                     DeviceCloseContext *ctx)
+{
+    MbimMessage *response;
+    GError *error = NULL;
+
+    response = mbim_device_command_finish (self, res, &error);
+    if (!response)
+        g_simple_async_result_take_error (ctx->result, error);
+    else if (!mbim_message_close_done_get_result (response, &error))
+        g_simple_async_result_take_error (ctx->result, error);
+    else if (!destroy_iochannel (self, &error))
+        g_simple_async_result_take_error (ctx->result, error);
+    else
+        g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+
+    if (response)
+        mbim_message_unref (response);
+    device_close_context_complete_and_free (ctx);
+}
+
+/**
+ * mbim_device_close:
+ * @self: a #MbimDevice.
+ * @timeout: maximum time, in seconds, to wait for the device to be closed.
+ * @cancellable: optional #GCancellable object, #NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the operation is finished.
+ * @user_data: the data to pass to callback function.
+ *
+ * Asynchronously closes a #MbimDevice for I/O.
+ *
+ * When the operation is finished @callback will be called. You can then call
+ * mbim_device_close_finish() to get the result of the operation.
+ */
+void
+mbim_device_close (MbimDevice          *self,
+                   guint                timeout,
+                   GCancellable        *cancellable,
+                   GAsyncReadyCallback  callback,
+                   gpointer             user_data)
+{
+    MbimMessage *request;
+    DeviceCloseContext *ctx;
+
+    g_return_if_fail (MBIM_IS_DEVICE (self));
+
+    ctx = g_slice_new (DeviceCloseContext);
+    ctx->self = g_object_ref (self);
+    ctx->result = g_simple_async_result_new (G_OBJECT (self),
+                                             callback,
+                                             user_data,
+                                             mbim_device_close);
+    ctx->timeout = timeout;
+    ctx->cancellable = (cancellable ? g_object_ref (cancellable) : NULL);
+
+    /* Already closed? */
+    if (!self->priv->iochannel) {
+        g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+        device_close_context_complete_and_free (ctx);
+        return;
+    }
+
+    /* If the device is in-session, avoid the close message */
+    if (self->priv->in_session) {
+        GError *error = NULL;
+
+        if (!destroy_iochannel (self, &error))
+            g_simple_async_result_take_error (ctx->result, error);
+        else
+            g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+        device_close_context_complete_and_free (ctx);
+        return;
+    }
+
+    /* Launch 'Close' command */
+    request = mbim_message_close_new (mbim_device_get_next_transaction_id (self));
+    mbim_device_command (self,
+                         request,
+                         10,
+                         ctx->cancellable,
+                         (GAsyncReadyCallback) close_message_ready,
+                         ctx);
+    mbim_message_unref (request);
+}
+
+/*****************************************************************************/
+
+/**
+ * mbim_device_get_next_transaction_id:
+ * @self: A #MbimDevice.
+ *
+ * Acquire the next transaction ID of this #MbimDevice.
+ * The internal transaction ID gets incremented.
+ *
+ * Returns: the next transaction ID.
+ */
+guint32
+mbim_device_get_next_transaction_id (MbimDevice *self)
+{
+    guint32 next;
+
+    g_return_val_if_fail (MBIM_IS_DEVICE (self), 0);
+
+    next = self->priv->transaction_id;
+
+    /* Don't go further than 8bits in the CTL service */
+    if (self->priv->transaction_id == G_MAXUINT32)
+        /* Reset! */
+        self->priv->transaction_id = 0x01;
+    else
+        self->priv->transaction_id++;
+
+    return next;
+}
+
+/*****************************************************************************/
+
+static gboolean
+device_write (MbimDevice    *self,
+              const guint8  *data,
+              guint32        data_length,
+              GError       **error)
+{
+    gsize written;
+    GIOStatus write_status;
+
+    written = 0;
+    write_status = G_IO_STATUS_AGAIN;
+    while (write_status == G_IO_STATUS_AGAIN) {
+        write_status = g_io_channel_write_chars (self->priv->iochannel,
+                                                 (gconstpointer)data,
+                                                 (gssize)data_length,
+                                                 &written,
+                                                 error);
+        switch (write_status) {
+        case G_IO_STATUS_ERROR:
+            g_prefix_error (error, "Cannot write message: ");
+            return FALSE;
+
+        case G_IO_STATUS_EOF:
+            /* We shouldn't get EOF when writing */
+            g_assert_not_reached ();
+            break;
+
+        case G_IO_STATUS_NORMAL:
+            /* All good, we'll exit the loop now */
+            break;
+
+        case G_IO_STATUS_AGAIN:
+            /* We're in a non-blocking channel and therefore we're up to receive
+             * EAGAIN; just retry in this case. TODO: in an idle? */
+            break;
+        }
+    }
+
+    return TRUE;
+}
+
+static gboolean
+device_send (MbimDevice   *self,
+             MbimMessage  *message,
+             GError      **error)
+{
+    const guint8 *raw_message;
+    guint32 raw_message_len;
+    struct fragment_info *fragments;
+    guint n_fragments;
+    guint i;
+
+    raw_message = mbim_message_get_raw (message, &raw_message_len, NULL);
+    g_assert (raw_message);
+
+    if (mbim_utils_get_traces_enabled ()) {
+        gchar *printable;
+
+        printable = __mbim_utils_str_hex (raw_message, raw_message_len, ':');
+        g_debug ("[%s] Sent message...\n"
+                 "<<<<<< RAW:\n"
+                 "<<<<<<   length = %u\n"
+                 "<<<<<<   data   = %s\n",
+                 self->priv->path_display,
+                 ((GByteArray *)message)->len,
+                 printable);
+        g_free (printable);
+
+        printable = mbim_message_get_printable (message, "<<<<<< ", FALSE);
+        g_debug ("[%s] Sent message (translated)...\n%s",
+                 self->priv->path_display,
+                 printable);
+        g_free (printable);
+    }
+
+    /* Single fragment? Send it! */
+    if (raw_message_len <= MAX_CONTROL_TRANSFER)
+        return device_write (self, raw_message, raw_message_len, error);
+
+    /* The message to send must be able to handle fragments */
+    g_assert (_mbim_message_is_fragment (message));
+
+    fragments = _mbim_message_split_fragments (message,
+                                               MAX_CONTROL_TRANSFER,
+                                               &n_fragments);
+    for (i = 0; i < n_fragments; i++) {
+        if (mbim_utils_get_traces_enabled ()) {
+            GByteArray *bytearray;
+            gchar *printable;
+            gchar *printable_h;
+            gchar *printable_fh;
+            gchar *printable_d;
+
+            printable_h  = __mbim_utils_str_hex (&fragments[i].header, sizeof (fragments[i].header), ':');
+            printable_fh = __mbim_utils_str_hex (&fragments[i].fragment_header, sizeof (fragments[i].fragment_header), ':');
+            printable_d  = __mbim_utils_str_hex (fragments[i].data, fragments[i].data_length, ':');
+            g_debug ("[%s] Sent fragment (%u)...\n"
+                     "<<<<<< RAW:\n"
+                     "<<<<<<   length = %u\n"
+                     "<<<<<<   data   = %s%s%s\n",
+                     self->priv->path_display, i,
+                     (guint)(sizeof (fragments[i].header) +
+                             sizeof (fragments[i].fragment_header) +
+                             fragments[i].data_length),
+                     printable_h, printable_fh, printable_d);
+            g_free (printable_h);
+            g_free (printable_fh);
+            g_free (printable_d);
+
+            /* Dummy message for printable purposes only */
+            bytearray = g_byte_array_new ();
+            g_byte_array_append (bytearray, (guint8 *)&fragments[i].header, sizeof (fragments[i].header));
+            g_byte_array_append (bytearray, (guint8 *)&fragments[i].fragment_header, sizeof (fragments[i].fragment_header));
+            printable = mbim_message_get_printable ((MbimMessage *)bytearray, "<<<<<< ", TRUE);
+            g_debug ("[%s] Sent fragment (translated)...\n%s",
+                     self->priv->path_display,
+                     printable);
+            g_free (printable);
+            g_byte_array_unref (bytearray);
+        }
+
+        /* Write fragment headers */
+        if (!device_write (self,
+                           (guint8 *)&fragments[i].header,
+                           sizeof (fragments[i].header),
+                           error))
+            return FALSE;
+
+        if (!device_write (self,
+                           (guint8 *)&fragments[i].fragment_header,
+                           sizeof (fragments[i].fragment_header),
+                           error))
+            return FALSE;
+
+        /* Write fragment data */
+        if (!device_write (self,
+                           fragments[i].data,
+                           fragments[i].data_length,
+                           error))
+            return FALSE;
+    }
+    g_free (fragments);
+
+    return TRUE;
+}
+
+/*****************************************************************************/
+/* Report error */
+
+typedef struct {
+    MbimDevice  *self;
+    MbimMessage *message;
+} ReportErrorContext;
+
+static void
+device_report_error_context_free (ReportErrorContext *ctx)
+{
+    mbim_message_unref (ctx->message);
+    g_object_unref (ctx->self);
+    g_slice_free (ReportErrorContext, ctx);
+}
+
+static gboolean
+device_report_error_in_idle (ReportErrorContext *ctx)
+{
+    /* Device must be open */
+    if (ctx->self->priv->iochannel) {
+        GError *error = NULL;
+
+        if (!device_send (ctx->self, ctx->message, &error)) {
+            g_warning ("[%s] Couldn't send host error message: %s",
+                       ctx->self->priv->path_display,
+                       error->message);
+            g_error_free (error);
+        }
+    }
+
+    device_report_error_context_free (ctx);
+    return FALSE;
+}
+
+static void
+device_report_error (MbimDevice   *self,
+                     guint32       transaction_id,
+                     const GError *error)
+{
+    ReportErrorContext *ctx;
+
+    /* Only protocol errors to be reported to the modem */
+    if (error->domain != MBIM_PROTOCOL_ERROR)
+        return;
+
+    ctx = g_slice_new (ReportErrorContext);
+    ctx->self = g_object_ref (self);
+    ctx->message = mbim_message_error_new (transaction_id, error->code);
+
+    g_idle_add ((GSourceFunc) device_report_error_in_idle, ctx);
+}
+
+/*****************************************************************************/
+/* Command */
+
+/**
+ * mbim_device_command_finish:
+ * @self: a #MbimDevice.
+ * @res: a #GAsyncResult.
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mbim_device_command().
+ *
+ * Returns: a #MbimMessage response, or #NULL if @error is set. The returned value should be freed with mbim_message_unref().
+ */
+MbimMessage *
+mbim_device_command_finish (MbimDevice    *self,
+                            GAsyncResult  *res,
+                            GError       **error)
+{
+    if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+        return NULL;
+
+    return mbim_message_ref (g_simple_async_result_get_op_res_gpointer (
+                                 G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+/**
+ * mbim_device_command:
+ * @self: a #MbimDevice.
+ * @message: the message to send.
+ * @timeout: maximum time, in seconds, to wait for the response.
+ * @cancellable: a #GCancellable, or %NULL.
+ * @callback: a #GAsyncReadyCallback to call when the operation is finished.
+ * @user_data: the data to pass to callback function.
+ *
+ * Asynchronously sends a #MbimMessage to the device.
+ *
+ * When the operation is finished @callback will be called. You can then call
+ * mbim_device_command_finish() to get the result of the operation.
+ */
+void
+mbim_device_command (MbimDevice          *self,
+                     MbimMessage         *message,
+                     guint                timeout,
+                     GCancellable        *cancellable,
+                     GAsyncReadyCallback  callback,
+                     gpointer             user_data)
+{
+    GError *error = NULL;
+    Transaction *tr;
+    guint32 transaction_id;
+
+    g_return_if_fail (MBIM_IS_DEVICE (self));
+    g_return_if_fail (message != NULL);
+
+    /* If the message comes without a explicit transaction ID, add one
+     * ourselves */
+    transaction_id = mbim_message_get_transaction_id (message);
+    if (!transaction_id) {
+        transaction_id = mbim_device_get_next_transaction_id (self);
+        mbim_message_set_transaction_id (message, transaction_id);
+    }
+
+    tr = transaction_new (self,
+                          transaction_id,
+                          cancellable,
+                          callback,
+                          user_data);
+
+    /* Device must be open */
+    if (!self->priv->iochannel) {
+        error = g_error_new (MBIM_CORE_ERROR,
+                             MBIM_CORE_ERROR_WRONG_STATE,
+                             "Device must be open to send commands");
+        transaction_complete_and_free (tr, error);
+        g_error_free (error);
+        return;
+    }
+
+    /* Setup context to match response */
+    if (!device_store_transaction (self, TRANSACTION_TYPE_HOST, tr, timeout * 1000, &error)) {
+        g_prefix_error (&error, "Cannot store transaction: ");
+        transaction_complete_and_free (tr, error);
+        g_error_free (error);
+        return;
+    }
+
+    if (!device_send (self, message, &error)) {
+        /* Match transaction so that we remove it from our tracking table */
+        tr = device_match_transaction (self, TRANSACTION_TYPE_HOST, message);
+        transaction_complete_and_free (tr, error);
+        g_error_free (error);
+        return;
+    }
+
+    /* Just return, we'll get response asynchronously */
+}
+
+/*****************************************************************************/
+/* New MBIM device */
+
+/**
+ * mbim_device_new_finish:
+ * @res: a #GAsyncResult.
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mbim_device_new().
+ *
+ * Returns: A newly created #MbimDevice, or #NULL if @error is set.
+ */
+MbimDevice *
+mbim_device_new_finish (GAsyncResult  *res,
+                        GError       **error)
+{
+  GObject *ret;
+  GObject *source_object;
+
+  source_object = g_async_result_get_source_object (res);
+  ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error);
+  g_object_unref (source_object);
+
+  return (ret ? MBIM_DEVICE (ret) : NULL);
+}
+
+/**
+ * mbim_device_new:
+ * @file: a #GFile.
+ * @cancellable: optional #GCancellable object, #NULL to ignore.
+ * @callback: a #GAsyncReadyCallback to call when the initialization is finished.
+ * @user_data: the data to pass to callback function.
+ *
+ * Asynchronously creates a #MbimDevice object to manage @file.
+ * When the operation is finished, @callback will be invoked. You can then call
+ * mbim_device_new_finish() to get the result of the operation.
+ */
+void
+mbim_device_new (GFile               *file,
+                 GCancellable        *cancellable,
+                 GAsyncReadyCallback  callback,
+                 gpointer             user_data)
+{
+    g_async_initable_new_async (MBIM_TYPE_DEVICE,
+                                G_PRIORITY_DEFAULT,
+                                cancellable,
+                                callback,
+                                user_data,
+                                MBIM_DEVICE_FILE, file,
+                                NULL);
+}
+
+/*****************************************************************************/
+/* Async init */
+
+typedef struct {
+    MbimDevice *self;
+    GSimpleAsyncResult *result;
+    GCancellable *cancellable;
+} InitContext;
+
+static void
+init_context_complete_and_free (InitContext *ctx)
+{
+    g_simple_async_result_complete_in_idle (ctx->result);
+    if (ctx->cancellable)
+        g_object_unref (ctx->cancellable);
+    g_object_unref (ctx->result);
+    g_object_unref (ctx->self);
+    g_slice_free (InitContext, ctx);
+}
+
+static gboolean
+initable_init_finish (GAsyncInitable  *initable,
+                      GAsyncResult    *result,
+                      GError         **error)
+{
+    return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+}
+
+static void
+query_info_async_ready (GFile *file,
+                        GAsyncResult *res,
+                        InitContext *ctx)
+{
+    GError *error = NULL;
+    GFileInfo *info;
+
+    info = g_file_query_info_finish (file, res, &error);
+    if (!info) {
+        g_prefix_error (&error,
+                        "Couldn't query file info: ");
+        g_simple_async_result_take_error (ctx->result, error);
+        init_context_complete_and_free (ctx);
+        return;
+    }
+
+    /* Our MBIM device must be of SPECIAL type */
+    if (g_file_info_get_file_type (info) != G_FILE_TYPE_SPECIAL) {
+        g_simple_async_result_set_error (ctx->result,
+                                         MBIM_CORE_ERROR,
+                                         MBIM_CORE_ERROR_FAILED,
+                                         "Wrong file type");
+        init_context_complete_and_free (ctx);
+        return;
+    }
+    g_object_unref (info);
+
+    /* Done we are */
+    g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+    init_context_complete_and_free (ctx);
+}
+
+static void
+initable_init_async (GAsyncInitable      *initable,
+                     int                  io_priority,
+                     GCancellable        *cancellable,
+                     GAsyncReadyCallback  callback,
+                     gpointer             user_data)
+{
+    InitContext *ctx;
+
+    ctx = g_slice_new0 (InitContext);
+    ctx->self = g_object_ref (initable);
+    if (cancellable)
+        ctx->cancellable = g_object_ref (cancellable);
+    ctx->result = g_simple_async_result_new (G_OBJECT (initable),
+                                             callback,
+                                             user_data,
+                                             initable_init_async);
+
+    /* We need a proper file to initialize */
+    if (!ctx->self->priv->file) {
+        g_simple_async_result_set_error (ctx->result,
+                                         MBIM_CORE_ERROR,
+                                         MBIM_CORE_ERROR_INVALID_ARGS,
+                                         "Cannot initialize MBIM device: No file given");
+        init_context_complete_and_free (ctx);
+        return;
+    }
+
+    /* Check the file type. Note that this is just a quick check to avoid
+     * creating MbimDevices pointing to a location already known not to be a MBIM
+     * device. */
+    g_file_query_info_async (ctx->self->priv->file,
+                             G_FILE_ATTRIBUTE_STANDARD_TYPE,
+                             G_FILE_QUERY_INFO_NONE,
+                             G_PRIORITY_DEFAULT,
+                             ctx->cancellable,
+                             (GAsyncReadyCallback)query_info_async_ready,
+                             ctx);
+}
+
+/*****************************************************************************/
+
+static void
+set_property (GObject      *object,
+              guint         prop_id,
+              const GValue *value,
+              GParamSpec   *pspec)
+{
+    MbimDevice *self = MBIM_DEVICE (object);
+
+    switch (prop_id) {
+    case PROP_FILE:
+        g_assert (self->priv->file == NULL);
+        self->priv->file = g_value_dup_object (value);
+        self->priv->path = g_file_get_path (self->priv->file);
+        self->priv->path_display = g_filename_display_name (self->priv->path);
+        break;
+    case PROP_TRANSACTION_ID:
+        self->priv->transaction_id = g_value_get_uint (value);
+        break;
+    case PROP_IN_SESSION:
+        self->priv->in_session = g_value_get_boolean (value);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+get_property (GObject    *object,
+              guint       prop_id,
+              GValue     *value,
+              GParamSpec *pspec)
+{
+    MbimDevice *self = MBIM_DEVICE (object);
+
+    switch (prop_id) {
+    case PROP_FILE:
+        g_value_set_object (value, self->priv->file);
+        break;
+    case PROP_TRANSACTION_ID:
+        g_value_set_uint (value, self->priv->transaction_id);
+        break;
+    case PROP_IN_SESSION:
+        g_value_set_boolean (value, self->priv->in_session);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+mbim_device_init (MbimDevice *self)
+{
+    self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
+                                              MBIM_TYPE_DEVICE,
+                                              MbimDevicePrivate);
+
+    /* Initialize transaction ID */
+    self->priv->transaction_id = 0x01;
+}
+
+static void
+dispose (GObject *object)
+{
+    MbimDevice *self = MBIM_DEVICE (object);
+
+    g_clear_object (&self->priv->file);
+
+    G_OBJECT_CLASS (mbim_device_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+    MbimDevice *self = MBIM_DEVICE (object);
+    guint i;
+
+    /* Transactions keep refs to the device, so it's actually
+     * impossible to have any content in the HT */
+    for (i = 0; i < TRANSACTION_TYPE_LAST; i++) {
+        if (self->priv->transactions[i]) {
+            g_assert (g_hash_table_size (self->priv->transactions[i]) == 0);
+            g_hash_table_unref (self->priv->transactions[i]);
+        }
+    }
+
+    g_free (self->priv->path);
+    g_free (self->priv->path_display);
+    if (self->priv->watch_id)
+        g_source_remove (self->priv->watch_id);
+    if (self->priv->response)
+        g_byte_array_unref (self->priv->response);
+    if (self->priv->iochannel)
+        g_io_channel_unref (self->priv->iochannel);
+
+    G_OBJECT_CLASS (mbim_device_parent_class)->finalize (object);
+}
+
+static void
+async_initable_iface_init (GAsyncInitableIface *iface)
+{
+    iface->init_async = initable_init_async;
+    iface->init_finish = initable_init_finish;
+}
+
+static void
+mbim_device_class_init (MbimDeviceClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    g_type_class_add_private (object_class, sizeof (MbimDevicePrivate));
+
+    object_class->get_property = get_property;
+    object_class->set_property = set_property;
+    object_class->finalize = finalize;
+    object_class->dispose = dispose;
+
+    properties[PROP_FILE] =
+        g_param_spec_object (MBIM_DEVICE_FILE,
+                             "Device file",
+                             "File to the underlying MBIM device",
+                             G_TYPE_FILE,
+                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+    g_object_class_install_property (object_class, PROP_FILE, properties[PROP_FILE]);
+
+    properties[PROP_TRANSACTION_ID] =
+        g_param_spec_uint (MBIM_DEVICE_TRANSACTION_ID,
+                           "Transaction ID",
+                           "Current transaction ID",
+                           0x01,
+                           G_MAXUINT32,
+                           0x01,
+                           G_PARAM_READWRITE);
+    g_object_class_install_property (object_class, PROP_TRANSACTION_ID, properties[PROP_TRANSACTION_ID]);
+
+    properties[PROP_IN_SESSION] =
+        g_param_spec_boolean (MBIM_DEVICE_IN_SESSION,
+                              "In session",
+                              "Flag to specify if the device is within a session",
+                              FALSE,
+                              G_PARAM_READWRITE);
+    g_object_class_install_property (object_class, PROP_IN_SESSION, properties[PROP_IN_SESSION]);
+
+  /**
+   * MbimDevice::device-indicate-status:
+   * @self: the #MbimDevice
+   * @message: the #MbimMessage indication
+   *
+   * The ::device-indication-status signal is emitted when a MBIM indication is received.
+   */
+    signals[SIGNAL_INDICATE_STATUS] =
+        g_signal_new (MBIM_DEVICE_SIGNAL_INDICATE_STATUS,
+                      G_OBJECT_CLASS_TYPE (G_OBJECT_CLASS (klass)),
+                      G_SIGNAL_RUN_LAST,
+                      0,
+                      NULL,
+                      NULL,
+                      NULL,
+                      G_TYPE_NONE,
+                      1,
+                      MBIM_TYPE_MESSAGE);
+
+  /**
+   * MbimDevice::device-error:
+   * @self: the #MbimDevice
+   * @message: the #MbimMessage error
+   *
+   * The ::device-error signal is emitted when a MBIM error is received.
+   */
+    signals[SIGNAL_ERROR] =
+        g_signal_new (MBIM_DEVICE_SIGNAL_ERROR,
+                      G_OBJECT_CLASS_TYPE (G_OBJECT_CLASS (klass)),
+                      G_SIGNAL_RUN_LAST,
+                      0,
+                      NULL,
+                      NULL,
+                      NULL,
+                      G_TYPE_NONE,
+                      1,
+                      G_TYPE_ERROR);
+}
diff --git a/libmbim-glib/mbim-device.h b/libmbim-glib/mbim-device.h
new file mode 100644
index 0000000..963571f
--- /dev/null
+++ b/libmbim-glib/mbim-device.h
@@ -0,0 +1,122 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@lanedo.com>
+ */
+
+#ifndef _LIBMBIM_GLIB_MBIM_DEVICE_H_
+#define _LIBMBIM_GLIB_MBIM_DEVICE_H_
+
+#if !defined (__LIBMBIM_GLIB_H_INSIDE__) && !defined (LIBMBIM_GLIB_COMPILATION)
+#error "Only <libmbim-glib.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "mbim-message.h"
+
+G_BEGIN_DECLS
+
+#define MBIM_TYPE_DEVICE            (mbim_device_get_type ())
+#define MBIM_DEVICE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), MBIM_TYPE_DEVICE, MbimDevice))
+#define MBIM_DEVICE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  MBIM_TYPE_DEVICE, MbimDeviceClass))
+#define MBIM_IS_DEVICE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MBIM_TYPE_DEVICE))
+#define MBIM_IS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  MBIM_TYPE_DEVICE))
+#define MBIM_DEVICE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  MBIM_TYPE_DEVICE, MbimDeviceClass))
+
+typedef struct _MbimDevice MbimDevice;
+typedef struct _MbimDeviceClass MbimDeviceClass;
+typedef struct _MbimDevicePrivate MbimDevicePrivate;
+
+#define MBIM_DEVICE_FILE           "device-file"
+#define MBIM_DEVICE_TRANSACTION_ID "device-transaction-id"
+#define MBIM_DEVICE_IN_SESSION     "device-in-session"
+
+#define MBIM_DEVICE_SIGNAL_INDICATE_STATUS "device-indicate-status"
+#define MBIM_DEVICE_SIGNAL_ERROR           "device-error"
+
+/**
+ * MbimDevice:
+ *
+ * The #MbimDevice structure contains private data and should only be accessed
+ * using the provided API.
+ */
+struct _MbimDevice {
+    /*< private >*/
+    GObject parent;
+    MbimDevicePrivate *priv;
+};
+
+struct _MbimDeviceClass {
+    /*< private >*/
+    GObjectClass parent;
+};
+
+GType mbim_device_get_type (void);
+
+void        mbim_device_new        (GFile                *file,
+                                    GCancellable         *cancellable,
+                                    GAsyncReadyCallback   callback,
+                                    gpointer              user_data);
+MbimDevice *mbim_device_new_finish (GAsyncResult         *res,
+                                    GError              **error);
+
+GFile       *mbim_device_get_file         (MbimDevice *self);
+GFile       *mbim_device_peek_file        (MbimDevice *self);
+const gchar *mbim_device_get_path         (MbimDevice *self);
+const gchar *mbim_device_get_path_display (MbimDevice *self);
+gboolean     mbim_device_is_open          (MbimDevice *self);
+
+void     mbim_device_open        (MbimDevice           *self,
+                                  guint                 timeout,
+                                  GCancellable         *cancellable,
+                                  GAsyncReadyCallback   callback,
+                                  gpointer              user_data);
+gboolean mbim_device_open_finish (MbimDevice           *self,
+                                  GAsyncResult         *res,
+                                  GError              **error);
+
+void     mbim_device_close        (MbimDevice           *self,
+                                   guint                 timeout,
+                                   GCancellable         *cancellable,
+                                   GAsyncReadyCallback   callback,
+                                   gpointer              user_data);
+gboolean mbim_device_close_finish (MbimDevice           *self,
+                                   GAsyncResult         *res,
+                                   GError              **error);
+
+gboolean mbim_device_close_force (MbimDevice *self,
+                                  GError **error);
+
+guint32 mbim_device_get_next_transaction_id (MbimDevice *self);
+
+void         mbim_device_command        (MbimDevice           *self,
+                                         MbimMessage          *message,
+                                         guint                 timeout,
+                                         GCancellable         *cancellable,
+                                         GAsyncReadyCallback   callback,
+                                         gpointer              user_data);
+MbimMessage *mbim_device_command_finish (MbimDevice           *self,
+                                         GAsyncResult         *res,
+                                         GError              **error);
+
+G_END_DECLS
+
+#endif /* _LIBMBIM_GLIB_MBIM_DEVICE_H_ */
diff --git a/libmbim-glib/mbim-enums.h b/libmbim-glib/mbim-enums.h
new file mode 100644
index 0000000..6275537
--- /dev/null
+++ b/libmbim-glib/mbim-enums.h
@@ -0,0 +1,922 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#ifndef _LIBMBIM_GLIB_MBIM_ENUMS_H_
+#define _LIBMBIM_GLIB_MBIM_ENUMS_H_
+
+#if !defined (__LIBMBIM_GLIB_H_INSIDE__) && !defined (LIBMBIM_GLIB_COMPILATION)
+#error "Only <libmbim-glib.h> can be included directly."
+#endif
+
+G_BEGIN_DECLS
+
+/**
+ * SECTION: mbim-enums
+ * @title: Enumerations and Flags
+ *
+ * This section defines common enum and flag types used in the interface.
+ */
+
+/*****************************************************************************/
+/* 'Device Caps' enums */
+
+/**
+ * MbimDeviceType:
+ * @MBIM_DEVICE_TYPE_UNKNOWN: Unknown type.
+ * @MBIM_DEVICE_TYPE_EMBEDDED: Device is embedded in the system.
+ * @MBIM_DEVICE_TYPE_REMOVABLE: Device is removable.
+ * @MBIM_DEVICE_TYPE_REMOTE: Device is remote.
+ *
+ * Type of device.
+ */
+typedef enum {
+    MBIM_DEVICE_TYPE_UNKNOWN   = 0,
+    MBIM_DEVICE_TYPE_EMBEDDED  = 1,
+    MBIM_DEVICE_TYPE_REMOVABLE = 2,
+    MBIM_DEVICE_TYPE_REMOTE    = 3
+} MbimDeviceType;
+
+/**
+ * MbimCellularClass:
+ * @MBIM_CELLULAR_CLASS_GSM: Device is 3GPP.
+ * @MBIM_CELLULAR_CLASS_CDMA: Device is 3GPP2.
+ *
+ * Cellular class.
+ */
+typedef enum {
+    MBIM_CELLULAR_CLASS_GSM  = 1 << 0,
+    MBIM_CELLULAR_CLASS_CDMA = 1 << 1
+} MbimCellularClass;
+
+/**
+ * MbimVoiceClass:
+ * @MBIM_VOICE_CLASS_UNKNOWN: Unknown voice class.
+ * @MBIM_VOICE_CLASS_NO_VOICE: Device doesn't support voice.
+ * @MBIM_VOICE_CLASS_SEPARATED_VOICE_DATA: Device supports separate voice and data connections.
+ * @MBIM_VOICE_CLASS_SIMULTANEOUS_VOICE_DATA: Device supports simultaneous voice and data connections.
+ *
+ * Voice class.
+ */
+typedef enum {
+    MBIM_VOICE_CLASS_UNKNOWN                 = 0,
+    MBIM_VOICE_CLASS_NO_VOICE                = 1,
+    MBIM_VOICE_CLASS_SEPARATED_VOICE_DATA    = 2,
+    MBIM_VOICE_CLASS_SIMULTANEOUS_VOICE_DATA = 3
+} MbimVoiceClass;
+
+/**
+ * MbimSimClass:
+ * @MBIM_SIM_CLASS_LOGICAL: No physical SIM.
+ * @MBIM_SIM_CLASS_REMOVABLE: Physical removable SIM.
+ *
+ * SIM class.
+ */
+typedef enum {
+    MBIM_SIM_CLASS_LOGICAL   = 1 << 0,
+    MBIM_SIM_CLASS_REMOVABLE = 1 << 1
+} MbimSimClass;
+
+/**
+ * MbimDataClass:
+ * @MBIM_DATA_CLASS_GPRS: GPRS.
+ * @MBIM_DATA_CLASS_EDGE: EDGE.
+ * @MBIM_DATA_CLASS_UMTS: UMTS.
+ * @MBIM_DATA_CLASS_HSDPA: HSDPA.
+ * @MBIM_DATA_CLASS_HSUPA: HSUPA.
+ * @MBIM_DATA_CLASS_LTE: LTE.
+ * @MBIM_DATA_CLASS_1XRTT: 1xRTT.
+ * @MBIM_DATA_CLASS_1XEVDO: 1xEV-DO.
+ * @MBIM_DATA_CLASS_1XEVDO_REVA: 1xEV-DO RevA
+ * @MBIM_DATA_CLASS_1XEVDV: 1xEV-DV.
+ * @MBIM_DATA_CLASS_3XRTT: 3xRTT.
+ * @MBIM_DATA_CLASS_1XEVDO_REVB: 1xEV-DO RevB.
+ * @MBIM_DATA_CLASS_UMB: UMB.
+ * @MBIM_DATA_CLASS_CUSTOM: Custom.
+ *
+ * Data class.
+ */
+typedef enum {
+    MBIM_DATA_CLASS_GPRS        = 1 << 0,
+    MBIM_DATA_CLASS_EDGE        = 1 << 1,
+    MBIM_DATA_CLASS_UMTS        = 1 << 2,
+    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_1XRTT       = 1 << 16,
+    MBIM_DATA_CLASS_1XEVDO      = 1 << 17,
+    MBIM_DATA_CLASS_1XEVDO_REVA = 1 << 18,
+    MBIM_DATA_CLASS_1XEVDV      = 1 << 19,
+    MBIM_DATA_CLASS_3XRTT       = 1 << 20,
+    MBIM_DATA_CLASS_1XEVDO_REVB = 1 << 21,
+    MBIM_DATA_CLASS_UMB         = 1 << 22,
+    /* Bits 23 to 30 reserved for future 3GPP2 classes */
+    MBIM_DATA_CLASS_CUSTOM      = 1 << 31
+} MbimDataClass;
+
+/**
+ * MbimSmsCaps:
+ * @MBIM_SMS_CAPS_PDU_RECEIVE: Can receive in PDU mode.
+ * @MBIM_SMS_CAPS_PDU_SEND: Can send in PDU mode.
+ * @MBIM_SMS_CAPS_TEXT_RECEIVE: Can receive in text mode.
+ * @MBIM_SMS_CAPS_TEXT_SEND: Can send in text mode.
+ *
+ * SMS capabilities.
+ */
+typedef enum {
+    MBIM_SMS_CAPS_PDU_RECEIVE  = 1 << 0,
+    MBIM_SMS_CAPS_PDU_SEND     = 1 << 1,
+    MBIM_SMS_CAPS_TEXT_RECEIVE = 1 << 2,
+    MBIM_SMS_CAPS_TEXT_SEND    = 1 << 3
+} MbimSmsCaps;
+
+/**
+ * MbimCtrlCaps:
+ * @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.
+ *
+ * Control capabilities.
+ */
+typedef enum {
+    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
+} MbimCtrlCaps;
+
+/*****************************************************************************/
+/* 'Subscriber Ready Status' enums */
+
+/**
+ * MbimSubscriberReadyState:
+ * @MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED: Not initialized.
+ * @MBIM_SUBSCRIBER_READY_STATE_INITIALIZED: Initialized.
+ * @MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED: SIM not inserted.
+ * @MBIM_SUBSCRIBER_READY_STATE_BAD_SIM: Bad SIM.
+ * @MBIM_SUBSCRIBER_READY_STATE_FAILURE: Failure.
+ * @MBIM_SUBSCRIBER_READY_STATE_NOT_ACTIVATED: Not activated.
+ * @MBIM_SUBSCRIBER_READY_STATE_DEVICE_LOCKED: Device locked.
+ *
+ * Ready state of the subscriber.
+ */
+typedef enum {
+    MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED  = 0,
+    MBIM_SUBSCRIBER_READY_STATE_INITIALIZED      = 1,
+    MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED = 2,
+    MBIM_SUBSCRIBER_READY_STATE_BAD_SIM          = 3,
+    MBIM_SUBSCRIBER_READY_STATE_FAILURE          = 4,
+    MBIM_SUBSCRIBER_READY_STATE_NOT_ACTIVATED    = 5,
+    MBIM_SUBSCRIBER_READY_STATE_DEVICE_LOCKED    = 6,
+} MbimSubscriberReadyState;
+
+/**
+ * MbimReadyInfoFlag:
+ * @MBIM_READY_INFO_FLAG_PROTECT_UNIQUE_ID: Request to avoid displaying subscriber ID.
+ */
+typedef enum {
+    MBIM_READY_INFO_FLAG_PROTECT_UNIQUE_ID = 1 << 0
+} MbimReadyInfoFlag;
+
+/*****************************************************************************/
+/* 'Radio State' enums */
+
+/**
+ * MbimRadioSwitchState:
+ * @MBIM_RADIO_SWITCH_STATE_OFF: Radio is off.
+ * @MBIM_RADIO_SWITCH_STATE_ON: Radio is on.
+ *
+ * Radio switch state.
+ */
+typedef enum {
+    MBIM_RADIO_SWITCH_STATE_OFF = 0,
+    MBIM_RADIO_SWITCH_STATE_ON  = 1
+} MbimRadioSwitchState;
+
+/*****************************************************************************/
+/* 'Pin' enums */
+
+/**
+ * MbimPinType:
+ * @MBIM_PIN_TYPE_UNKNOWN: Unknown or unset.
+ * @MBIM_PIN_TYPE_CUSTOM: The PIN type is a custom type and is none of the other PIN types listed in this enumeration.
+ * @MBIM_PIN_TYPE_PIN1: The PIN1 key.
+ * @MBIM_PIN_TYPE_PIN2: The PIN2 key.
+ * @MBIM_PIN_TYPE_DEVICE_SIM_PIN: The device to SIM key.
+ * @MBIM_PIN_TYPE_DEVICE_FIRST_SIM_PIN: The device to very first SIM key.
+ * @MBIM_PIN_TYPE_NETWORK_PIN: The network personalization key.
+ * @MBIM_PIN_TYPE_NETWORK_SUBSET_PIN: The network subset personalization key.
+ * @MBIM_PIN_TYPE_SERVICE_PROVIDER_PIN: The service provider (SP) personalization key.
+ * @MBIM_PIN_TYPE_CORPORATE_PIN: The corporate personalization key.
+ * @MBIM_PIN_TYPE_SUBSIDY_PIN: The subsidy unlock key.
+ * @MBIM_PIN_TYPE_PUK1: The Personal Identification Number1 Unlock Key (PUK1).
+ * @MBIM_PIN_TYPE_PUK2: The Personal Identification Number2 Unlock Key (PUK2).
+ * @MBIM_PIN_TYPE_DEVICE_FIRST_SIM_PUK: The device to very first SIM PIN unlock key.
+ * @MBIM_PIN_TYPE_NETWORK_PUK: The network personalization unlock key.
+ * @MBIM_PIN_TYPE_NETWORK_SUBSET_PUK: The network subset personalization unlock key.
+ * @MBIM_PIN_TYPE_SERVICE_PROVIDER_PUK: The service provider (SP) personalization unlock key.
+ * @MBIM_PIN_TYPE_CORPORATE_PUK: The corporate personalization unlock key.
+ *
+ * PIN Types.
+ */
+typedef enum {
+    MBIM_PIN_TYPE_UNKNOWN              = 0,
+    MBIM_PIN_TYPE_CUSTOM               = 1,
+    MBIM_PIN_TYPE_PIN1                 = 2,
+    MBIM_PIN_TYPE_PIN2                 = 3,
+    MBIM_PIN_TYPE_DEVICE_SIM_PIN       = 4,
+    MBIM_PIN_TYPE_DEVICE_FIRST_SIM_PIN = 5,
+    MBIM_PIN_TYPE_NETWORK_PIN          = 6,
+    MBIM_PIN_TYPE_NETWORK_SUBSET_PIN   = 7,
+    MBIM_PIN_TYPE_SERVICE_PROVIDER_PIN = 8,
+    MBIM_PIN_TYPE_CORPORATE_PIN        = 9,
+    MBIM_PIN_TYPE_SUBSIDY_PIN          = 10,
+    MBIM_PIN_TYPE_PUK1                 = 11,
+    MBIM_PIN_TYPE_PUK2                 = 12,
+    MBIM_PIN_TYPE_DEVICE_FIRST_SIM_PUK = 13,
+    MBIM_PIN_TYPE_NETWORK_PUK          = 14,
+    MBIM_PIN_TYPE_NETWORK_SUBSET_PUK   = 15,
+    MBIM_PIN_TYPE_SERVICE_PROVIDER_PUK = 16,
+    MBIM_PIN_TYPE_CORPORATE_PUK        = 17
+} MbimPinType;
+
+/**
+ * MbimPinState:
+ * @MBIM_PIN_STATE_UNLOCKED: The device does not require a PIN.
+ * @MBIM_PIN_STATE_LOCKED: The device requires the user to enter a PIN.
+ *
+ * PIN States.
+ */
+typedef enum {
+    MBIM_PIN_STATE_UNLOCKED = 0,
+    MBIM_PIN_STATE_LOCKED   = 1
+} MbimPinState;
+
+/**
+ * MbimPinOperation:
+ * @MBIM_PIN_OPERATION_ENTER: Enter the specified PIN into the device.
+ * @MBIM_PIN_OPERATION_ENABLE: Enable the specified PIN.
+ * @MBIM_PIN_OPERATION_DISABLE: Disable the specified PIN.
+ * @MBIM_PIN_OPERATION_CHANGE:  Change the specified PIN.
+*/
+typedef enum {
+    MBIM_PIN_OPERATION_ENTER   = 0,
+    MBIM_PIN_OPERATION_ENABLE  = 1,
+    MBIM_PIN_OPERATION_DISABLE = 2,
+    MBIM_PIN_OPERATION_CHANGE  = 3
+} MbimPinOperation;
+
+/*****************************************************************************/
+/* 'Pin List' enums */
+
+/**
+ * MbimPinMode:
+ * @MBIM_PIN_MODE_NOT_SUPPORTED: Not supported.
+ * @MBIM_PIN_MODE_ENABLED: Enabled.
+ * @MBIM_PIN_MODE_DISABLED: Disabled.
+ *
+ * Whether the lock is enabled or disabled.
+ */
+typedef enum {
+    MBIM_PIN_MODE_NOT_SUPPORTED = 0,
+    MBIM_PIN_MODE_ENABLED       = 1,
+    MBIM_PIN_MODE_DISABLED      = 2
+} MbimPinMode;
+
+/**
+ * MbimPinFormat:
+ * @MBIM_PIN_FORMAT_UNKNOWN: Unknown format.
+ * @MBIM_PIN_FORMAT_NUMERIC: Numeric-only format.
+ * @MBIM_PIN_FORMAT_ALPHANUMERIC: Alphanumeric format.
+ *
+ * Format of the expected PIN code.
+ */
+typedef enum {
+    MBIM_PIN_FORMAT_UNKNOWN      = 0,
+    MBIM_PIN_FORMAT_NUMERIC      = 1,
+    MBIM_PIN_FORMAT_ALPHANUMERIC = 2
+} MbimPinFormat;
+
+/*****************************************************************************/
+/* 'Home Provider' enums */
+
+/**
+ * MbimProviderState:
+ * @MBIM_PROVIDER_STATE_UNKNOWN: Unknown.
+ * @MBIM_PROVIDER_STATE_HOME: Home operator.
+ * @MBIM_PROVIDER_STATE_FORBIDDEN: Provider blocked.
+ * @MBIM_PROVIDER_STATE_PREFERRED: Provider is in the preferred list.
+ * @MBIM_PROVIDER_STATE_VISIBLE: Provider is visible.
+ * @MBIM_PROVIDER_STATE_REGISTERED: Currently registered to the provider.
+ * @MBIM_PROVIDER_STATE_PREFERRED_MULTICARRIER: Provider is a preferred multicarrier network.
+ *
+ * State of the provider.
+ */
+typedef enum {
+    MBIM_PROVIDER_STATE_UNKNOWN                = 0,
+    MBIM_PROVIDER_STATE_HOME                   = 1 << 0,
+    MBIM_PROVIDER_STATE_FORBIDDEN              = 1 << 1,
+    MBIM_PROVIDER_STATE_PREFERRED              = 1 << 2,
+    MBIM_PROVIDER_STATE_VISIBLE                = 1 << 3,
+    MBIM_PROVIDER_STATE_REGISTERED             = 1 << 4,
+    MBIM_PROVIDER_STATE_PREFERRED_MULTICARRIER = 1 << 5
+} MbimProviderState;
+
+/*****************************************************************************/
+/* 'Visible Providers' enums */
+
+/**
+ * MbimVisibleProvidersAction:
+ * @MBIM_VISIBLE_PROVIDERS_ACTION_FULL_SCAN: Full scan.
+ * @MBIM_VISIBLE_PROVIDERS_ACTION_RESTRICTED_SCAN: Locate preferred multicarrier providers.
+ *
+ * Type of action to perform when listing visible providers.
+ */
+typedef enum {
+    MBIM_VISIBLE_PROVIDERS_ACTION_FULL_SCAN       = 0,
+    MBIM_VISIBLE_PROVIDERS_ACTION_RESTRICTED_SCAN = 1
+} MbimVisibleProvidersAction;
+
+/*****************************************************************************/
+/* 'Register State' enums */
+
+/**
+ * MbimNwError:
+ * @MBIM_NW_ERROR_UNKNOWN: Unknown or unset error.
+ * @MBIM_NW_ERROR_IMSI_UNKNOWN_IN_HLR: IMSI unknown in the HLR.
+ * @MBIM_NW_ERROR_IMSI_UNKNOWN_IN_VLR: IMSI unknown in the VLR.
+ * @MBIM_NW_ERROR_ILLEGAL_ME: Illegal ME.
+ * @MBIM_NW_ERROR_GPRS_NOT_ALLOWED: GPRS not allowed.
+ * @MBIM_NW_ERROR_GPRS_AND_NON_GPRS_NOT_ALLOWED: GPRS and non-GPRS not allowed.
+ * @MBIM_NW_ERROR_PLMN_NOT_ALLOWED: PLMN not allowed.
+ * @MBIM_NW_ERROR_LOCATION_AREA_NOT_ALLOWED: Location area not allowed.
+ * @MBIM_NW_ERROR_ROAMING_NOT_ALLOWED_IN_LOCATION_AREA: Roaming not allowed in the location area.
+ * @MBIM_NW_ERROR_GPRS_NOT_ALLOWED_IN_PLMN: GPRS not allowed in PLMN.
+ * @MBIM_NW_ERROR_NO_CELLS_IN_LOCATION_AREA: No cells in location area.
+ * @MBIM_NW_ERROR_NETWORK_FAILURE: Network failure.
+ * @MBIM_NW_ERROR_CONGESTION: Congestion.
+ *
+ *  Network errors.
+ */
+typedef enum {
+    MBIM_NW_ERROR_UNKNOWN                              = 0,
+    MBIM_NW_ERROR_IMSI_UNKNOWN_IN_HLR                  = 2,
+    MBIM_NW_ERROR_IMSI_UNKNOWN_IN_VLR                  = 4,
+    MBIM_NW_ERROR_ILLEGAL_ME                           = 6,
+    MBIM_NW_ERROR_GPRS_NOT_ALLOWED                     = 7,
+    MBIM_NW_ERROR_GPRS_AND_NON_GPRS_NOT_ALLOWED        = 8,
+    MBIM_NW_ERROR_PLMN_NOT_ALLOWED                     = 11,
+    MBIM_NW_ERROR_LOCATION_AREA_NOT_ALLOWED            = 12,
+    MBIM_NW_ERROR_ROAMING_NOT_ALLOWED_IN_LOCATION_AREA = 13,
+    MBIM_NW_ERROR_GPRS_NOT_ALLOWED_IN_PLMN             = 14,
+    MBIM_NW_ERROR_NO_CELLS_IN_LOCATION_AREA            = 15,
+    MBIM_NW_ERROR_NETWORK_FAILURE                      = 17,
+    MBIM_NW_ERROR_CONGESTION                           = 22
+} MbimNwError;
+
+/**
+ * MbimRegisterAction:
+ * @MBIM_REGISTER_ACTION_AUTOMATIC: Automatic registration.
+ * @MBIM_REGISTER_ACTION_MANUAL: Manual registration.
+ *
+ * Type of registration requested.
+ */
+typedef enum {
+    MBIM_REGISTER_ACTION_AUTOMATIC = 0,
+    MBIM_REGISTER_ACTION_MANUAL    = 1
+} MbimRegisterAction;
+
+/**
+ * MbimRegisterState:
+ * @MBIM_REGISTER_STATE_UNKNOWN: Unknown registration state.
+ * @MBIM_REGISTER_STATE_DEREGISTERED: Not registered.
+ * @MBIM_REGISTER_STATE_SEARCHING: Searching.
+ * @MBIM_REGISTER_STATE_HOME: Registered in home network.
+ * @MBIM_REGISTER_STATE_ROAMING: Registered in roaming network.
+ * @MBIM_REGISTER_STATE_PARTNER: Registered in a preferred roaming network.
+ * @MBIM_REGISTER_STATE_DENIED: Registration denied.
+ *
+ * Registration state.
+ */
+typedef enum {
+    MBIM_REGISTER_STATE_UNKNOWN      = 0,
+    MBIM_REGISTER_STATE_DEREGISTERED = 1,
+    MBIM_REGISTER_STATE_SEARCHING    = 2,
+    MBIM_REGISTER_STATE_HOME         = 3,
+    MBIM_REGISTER_STATE_ROAMING      = 4,
+    MBIM_REGISTER_STATE_PARTNER      = 5,
+    MBIM_REGISTER_STATE_DENIED       = 6
+} MbimRegisterState;
+
+/**
+ * MbimRegisterMode:
+ * @MBIM_REGISTER_MODE_UNKNOWN: Unknown.
+ * @MBIM_REGISTER_MODE_AUTOMATIC: Automatic registration.
+ * @MBIM_REGISTER_MODE_MANUAL: Manual registration.
+ *
+ * Type of registration requested.
+ */
+typedef enum {
+    MBIM_REGISTER_MODE_UNKNOWN   = 0,
+    MBIM_REGISTER_MODE_AUTOMATIC = 1,
+    MBIM_REGISTER_MODE_MANUAL    = 2
+} MbimRegisterMode;
+
+/**
+ * MbimRegistrationFlag:
+ * @MBIM_REGISTRATION_FLAG_NONE: None.
+ * @MBIM_REGISTRATION_FLAG_MANUAL_SELECTION_NOT_AVAILABLE: Network doesn't support manual network selection.
+ * @MBIM_REGISTRATION_FLAG_MANUAL_PACKET_SERVICE_AUTOMATIC_ATTACH: Modem should auto-attach to the network after registration.
+ *
+ * Registration flags.
+ */
+typedef enum {
+    MBIM_REGISTRATION_FLAG_NONE                                   = 0,
+    MBIM_REGISTRATION_FLAG_MANUAL_SELECTION_NOT_AVAILABLE         = 1 << 0,
+    MBIM_REGISTRATION_FLAG_MANUAL_PACKET_SERVICE_AUTOMATIC_ATTACH = 1 << 2,
+} MbimRegistrationFlag;
+
+/*****************************************************************************/
+/* 'Packet Service' enums */
+
+/**
+ * MbimPacketServiceAction:
+ * @MBIM_PACKET_SERVICE_ACTION_ATTACH: Attach.
+ * @MBIM_PACKET_SERVICE_ACTION_DETACH: Detach.
+ *
+ * Packet Service Action.
+ */
+typedef enum {
+    MBIM_PACKET_SERVICE_ACTION_ATTACH = 0,
+    MBIM_PACKET_SERVICE_ACTION_DETACH = 1
+} MbimPacketServiceAction;
+
+/**
+ * MbimPacketServiceState:
+ * @MBIM_PACKET_SERVICE_STATE_UNKNOWN: Unknown.
+ * @MBIM_PACKET_SERVICE_STATE_ATTACHING: Attaching.
+ * @MBIM_PACKET_SERVICE_STATE_ATTACHED: Attached.
+ * @MBIM_PACKET_SERVICE_STATE_DETACHING: Detaching.
+ * @MBIM_PACKET_SERVICE_STATE_DETACHED: Detached.
+ *
+ * Packet Service State.
+ */
+typedef enum {
+    MBIM_PACKET_SERVICE_STATE_UNKNOWN   = 0,
+    MBIM_PACKET_SERVICE_STATE_ATTACHING = 1,
+    MBIM_PACKET_SERVICE_STATE_ATTACHED  = 2,
+    MBIM_PACKET_SERVICE_STATE_DETACHING = 3,
+    MBIM_PACKET_SERVICE_STATE_DETACHED  = 4
+} MbimPacketServiceState;
+
+/*****************************************************************************/
+/* 'Connect' enums */
+
+/**
+ * MbimActivationCommand:
+ * @MBIM_ACTIVATION_COMMAND_DEACTIVATE: Deactivate.
+ * @MBIM_ACTIVATION_COMMAND_ACTIVATE: Activate.
+ *
+ * Activation Command.
+ */
+typedef enum {
+    MBIM_ACTIVATION_COMMAND_DEACTIVATE = 0,
+    MBIM_ACTIVATION_COMMAND_ACTIVATE   = 1
+} MbimActivationCommand;
+
+/**
+ * MbimCompression:
+ * @MBIM_COMPRESSION_NONE: None.
+ * @MBIM_COMPRESSION_ENABLE: Enable.
+ *
+ * Compression.
+ */
+typedef enum {
+    MBIM_COMPRESSION_NONE   = 0,
+    MBIM_COMPRESSION_ENABLE = 1
+} MbimCompression;
+
+/**
+ * MbimAuthProtocol:
+ * @MBIM_AUTH_PROTOCOL_NONE: None.
+ * @MBIM_AUTH_PROTOCOL_PAP: Pap.
+ * @MBIM_AUTH_PROTOCOL_CHAP: Chap.
+ * @MBIM_AUTH_PROTOCOL_MSCHAPV2: V2.
+ *
+ * Auth Protocol.
+ */
+typedef enum {
+    MBIM_AUTH_PROTOCOL_NONE     = 0,
+    MBIM_AUTH_PROTOCOL_PAP      = 1,
+    MBIM_AUTH_PROTOCOL_CHAP     = 2,
+    MBIM_AUTH_PROTOCOL_MSCHAPV2 = 3
+} MbimAuthProtocol;
+
+/**
+ * MbimContextIpType:
+ * @MBIM_CONTEXT_IP_TYPE_DEFAULT: It is up to the function to decide, the host does not care.
+ * @MBIM_CONTEXT_IP_TYPE_IPV4: IPv4 context.
+ * @MBIM_CONTEXT_IP_TYPE_IPV6: IPv6 context.
+ * @MBIM_CONTEXT_IP_TYPE_IPV4V6: The context is IPv4, IPv6 or dualstack IPv4v6.
+ * @MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6: Both an IPv4 and an IPv6 context.
+ *
+ * Context IP Type.
+ */
+typedef enum {
+    MBIM_CONTEXT_IP_TYPE_DEFAULT       = 0,
+    MBIM_CONTEXT_IP_TYPE_IPV4          = 1,
+    MBIM_CONTEXT_IP_TYPE_IPV6          = 2,
+    MBIM_CONTEXT_IP_TYPE_IPV4V6        = 3,
+    MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6 = 4
+} MbimContextIpType;
+
+/**
+ * MbimActivationState:
+ * @MBIM_ACTIVATION_STATE_UNKNOWN: Unknown.
+ * @MBIM_ACTIVATION_STATE_ACTIVATED: Activated.
+ * @MBIM_ACTIVATION_STATE_ACTIVATING: Activating.
+ * @MBIM_ACTIVATION_STATE_DEACTIVATED: Deactivated.
+ * @MBIM_ACTIVATION_STATE_DEACTIVATING: Deactivating.
+ *
+ * Activation State.
+ */
+typedef enum {
+    MBIM_ACTIVATION_STATE_UNKNOWN      = 0,
+    MBIM_ACTIVATION_STATE_ACTIVATED    = 1,
+    MBIM_ACTIVATION_STATE_ACTIVATING   = 2,
+    MBIM_ACTIVATION_STATE_DEACTIVATED  = 3,
+    MBIM_ACTIVATION_STATE_DEACTIVATING = 4
+} MbimActivationState;
+
+/**
+ * MbimVoiceCallState:
+ * @MBIM_VOICE_CALL_STATE_NONE: None.
+ * @MBIM_VOICE_CALL_STATE_IN_PROGRESS: Progress.
+ * @MBIM_VOICE_CALL_STATE_HANG_UP: Up.
+ *
+ * Voice Call State.
+ */
+typedef enum {
+    MBIM_VOICE_CALL_STATE_NONE        = 0,
+    MBIM_VOICE_CALL_STATE_IN_PROGRESS = 1,
+    MBIM_VOICE_CALL_STATE_HANG_UP     = 2
+} MbimVoiceCallState;
+
+/*****************************************************************************/
+/* 'IP Configuration' enums */
+
+/**
+ * MbimIPConfigurationAvailableFlag:
+ * @MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_NONE: No info available.
+ * @MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS: Address info available.
+ * @MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY: Gateway info available.
+ * @MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS: DNS info available.
+ * @MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU: MTU info available.
+ *
+ * Mask of available information about an IP address.
+ */
+typedef enum { /*< underscore_name=mbim_ip_configuration_available_flag >*/
+    MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_NONE     = 0,
+    MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS  = 1 << 0,
+    MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY  = 1 << 1,
+    MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS      = 1 << 2,
+    MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU      = 1 << 3,
+} MbimIPConfigurationAvailableFlag;
+
+/*****************************************************************************/
+/* 'SMS Configuration' enums */
+
+/**
+ * MbimSmsStorageState:
+ * @MBIM_SMS_STORAGE_STATE_NOT_INITIALIZED: Storage not initialized.
+ * @MBIM_SMS_STORAGE_STATE_INITIALIZED: Storage initialized.
+ *
+ * State of the SMS storage.
+ */
+typedef enum {
+    MBIM_SMS_STORAGE_STATE_NOT_INITIALIZED = 0,
+    MBIM_SMS_STORAGE_STATE_INITIALIZED     = 1
+} MbimSmsStorageState;
+
+/**
+ * MbimSmsFormat:
+ * @MBIM_SMS_FORMAT_PDU: PDU format.
+ * @MBIM_SMS_FORMAT_CDMA: CDMA format.
+ *
+ * SMS format.
+ */
+typedef enum {
+    MBIM_SMS_FORMAT_PDU  = 0,
+    MBIM_SMS_FORMAT_CDMA = 1
+} MbimSmsFormat;
+
+/*****************************************************************************/
+/* 'SMS Read' enums */
+
+/**
+ * MbimSmsFlag:
+ * @MBIM_SMS_FLAG_ALL: All.
+ * @MBIM_SMS_FLAG_INDEX: Index.
+ * @MBIM_SMS_FLAG_NEW: New.
+ * @MBIM_SMS_FLAG_OLD: Old.
+ * @MBIM_SMS_FLAG_SENT: Sent.
+ * @MBIM_SMS_FLAG_DRAFT: Draft.
+ *
+ * Flags to use when requesting to read SMS. @MBIM_SMS_FLAG_ALL and
+ * @MBIM_SMS_FLAG_NEW are mandatory, all the others are optional.
+*/
+typedef enum {
+    MBIM_SMS_FLAG_ALL   = 0,
+    MBIM_SMS_FLAG_INDEX = 1,
+    MBIM_SMS_FLAG_NEW   = 2,
+    MBIM_SMS_FLAG_OLD   = 3,
+    MBIM_SMS_FLAG_SENT  = 4,
+    MBIM_SMS_FLAG_DRAFT = 5
+} MbimSmsFlag;
+
+/**
+ * MbimSmsCdmaLang:
+ * @MBIM_SMS_CDMA_LANG_UNKNOWN: Unknown language.
+ * @MBIM_SMS_CDMA_LANG_ENGLISH: English.
+ * @MBIM_SMS_CDMA_LANG_FRENCH: French.
+ * @MBIM_SMS_CDMA_LANG_SPANISH: Spanish.
+ * @MBIM_SMS_CDMA_LANG_JAPANESE: Japanese.
+ * @MBIM_SMS_CDMA_LANG_KOREAN: Korean.
+ * @MBIM_SMS_CDMA_LANG_CHINESE: Chinese.
+ * @MBIM_SMS_CDMA_LANG_HEBREW: Hebrew.
+ *
+ * Language of a CDMA SMS.
+ */
+typedef enum {
+    MBIM_SMS_CDMA_LANG_UNKNOWN  = 0,
+    MBIM_SMS_CDMA_LANG_ENGLISH  = 1,
+    MBIM_SMS_CDMA_LANG_FRENCH   = 2,
+    MBIM_SMS_CDMA_LANG_SPANISH  = 3,
+    MBIM_SMS_CDMA_LANG_JAPANESE = 4,
+    MBIM_SMS_CDMA_LANG_KOREAN   = 5,
+    MBIM_SMS_CDMA_LANG_CHINESE  = 6,
+    MBIM_SMS_CDMA_LANG_HEBREW   = 7
+} MbimSmsCdmaLang;
+
+/**
+ * MbimSmsCdmaEncoding:
+ * @MBIM_SMS_CDMA_ENCODING_OCTET: Octet.
+ * @MBIM_SMS_CDMA_ENCODING_EPM: EPM.
+ * @MBIM_SMS_CDMA_ENCODING_7BIT_ASCII: 7-bit ASCII.
+ * @MBIM_SMS_CDMA_ENCODING_LA5: LA5.
+ * @MBIM_SMS_CDMA_ENCODING_UNICODE: Unicode.
+ * @MBIM_SMS_CDMA_ENCODING_SHIFT_JIS: Shift JIS.
+ * @MBIM_SMS_CDMA_ENCODING_KOREAN: Korean.
+ * @MBIM_SMS_CDMA_ENCODING_LATIN_HEBREW: Latin hebrew.
+ * @MBIM_SMS_CDMA_ENCODING_LATIN: Latin.
+ * @MBIM_SMS_CDMA_ENCODING_GSM_7BIT: 7-bit GSM.
+ *
+ * Type of encoding of a CDMA SMS.
+ */
+typedef enum {
+    MBIM_SMS_CDMA_ENCODING_OCTET        = 0,
+    MBIM_SMS_CDMA_ENCODING_EPM          = 1,
+    MBIM_SMS_CDMA_ENCODING_7BIT_ASCII   = 2,
+    MBIM_SMS_CDMA_ENCODING_LA5          = 3,
+    MBIM_SMS_CDMA_ENCODING_UNICODE      = 4,
+    MBIM_SMS_CDMA_ENCODING_SHIFT_JIS    = 5,
+    MBIM_SMS_CDMA_ENCODING_KOREAN       = 6,
+    MBIM_SMS_CDMA_ENCODING_LATIN_HEBREW = 7,
+    MBIM_SMS_CDMA_ENCODING_LATIN        = 8,
+    MBIM_SMS_CDMA_ENCODING_GSM_7BIT     = 9
+} MbimSmsCdmaEncoding;
+
+/**
+ * MbimSmsStatus:
+ * @MBIM_SMS_STATUS_NEW: New.
+ * @MBIM_SMS_STATUS_OLD: Old.
+ * @MBIM_SMS_STATUS_DRAFT: Draft.
+ * @MBIM_SMS_STATUS_SENT: Sent.
+ *
+ * Status of a SMS message.
+ */
+typedef enum {
+    MBIM_SMS_STATUS_NEW   = 0,
+    MBIM_SMS_STATUS_OLD   = 1,
+    MBIM_SMS_STATUS_DRAFT = 2,
+    MBIM_SMS_STATUS_SENT  = 3
+} MbimSmsStatus;
+
+/*****************************************************************************/
+/* 'SMS Message Store Status' enums */
+
+/**
+ * MbimSmsStatusFlag:
+ * @MBIM_SMS_STATUS_FLAG_NONE: None.
+ * @MBIM_SMS_STATUS_FLAG_MESSAGE_STORE_FULL: Message store is full.
+ * @MBIM_SMS_STATUS_FLAG_NEW_MESSAGE: New non-Class 0 message arrived.
+ *
+ * SMS status flags.
+ */
+typedef enum {
+    MBIM_SMS_STATUS_FLAG_NONE               = 0,
+    MBIM_SMS_STATUS_FLAG_MESSAGE_STORE_FULL = 1,
+    MBIM_SMS_STATUS_FLAG_NEW_MESSAGE        = 2
+} MbimSmsStatusFlag;
+
+/*****************************************************************************/
+/* 'USSD' enums */
+
+/**
+ * MbimUssdAction:
+ * @MBIM_USSD_ACTION_INITIATE: Initiate USSD session.
+ * @MBIM_USSD_ACTION_CONTINUE: Continue USSD session.
+ * @MBIM_USSD_ACTION_CANCEL: Cancel USSD session.
+ *
+ * USSD action.
+ */
+typedef enum {
+    MBIM_USSD_ACTION_INITIATE = 0,
+    MBIM_USSD_ACTION_CONTINUE = 1,
+    MBIM_USSD_ACTION_CANCEL = 2
+} MbimUssdAction;
+
+/**
+ * MbimUssdResponse:
+ * @MBIM_USSD_RESPONSE_NO_ACTION_REQUIRED: No action required.
+ * @MBIM_USSD_RESPONSE_ACTION_REQUIRED: An action is required.
+ * @MBIM_USSD_RESPONSE_TERMINATED_BY_NETWORK: Terminated by network
+ * @MBIM_USSD_RESPONSE_OTHER_LOCAL_CLIENT: Other local client.
+ * @MBIM_USSD_RESPONSE_OPERATION_NOT_SUPPORTED: Operation not supported.
+ * @MBIM_USSD_RESPONSE_NETWORK_TIMEOUT: Network timeout.
+ *
+ * USSD response.
+ */
+typedef enum {
+    MBIM_USSD_RESPONSE_NO_ACTION_REQUIRED      = 0,
+    MBIM_USSD_RESPONSE_ACTION_REQUIRED         = 1,
+    MBIM_USSD_RESPONSE_TERMINATED_BY_NETWORK   = 2,
+    MBIM_USSD_RESPONSE_OTHER_LOCAL_CLIENT      = 3,
+    MBIM_USSD_RESPONSE_OPERATION_NOT_SUPPORTED = 4,
+    MBIM_USSD_RESPONSE_NETWORK_TIMEOUT         = 5
+} MbimUssdResponse;
+
+/**
+ * MbimUssdSessionState:
+ * @MBIM_USSD_SESSION_STATE_NEW_SESSION: New session.
+ * @MBIM_USSD_SESSION_STATE_EXISTING_SESSION: Existing session.
+ *
+ * Session state.
+ */
+typedef enum {
+    MBIM_USSD_SESSION_STATE_NEW_SESSION      = 0,
+    MBIM_USSD_SESSION_STATE_EXISTING_SESSION = 1
+} MbimUssdSessionState;
+
+/*****************************************************************************/
+/* 'Phonebook configuration' enums */
+
+/**
+ * MbimPhonebookState:
+ * @MBIM_PHONEBOOK_STATE_NOT_INITIALIZED: Not initialized.
+ * @MBIM_PHONEBOOK_STATE_INITIALIZED: Initialized
+ *
+ * Phonebook state.
+ */
+typedef enum {
+    MBIM_PHONEBOOK_STATE_NOT_INITIALIZED = 0,
+    MBIM_PHONEBOOK_STATE_INITIALIZED     = 1
+} MbimPhonebookState;
+
+/*****************************************************************************/
+/* 'Phonebook read' enums */
+
+/**
+ * MbimPhonebookFlag:
+ * @MBIM_PHONEBOOK_FLAG_ALL: Request all.
+ * @MBIM_PHONEBOOK_FLAG_INDEX: Request single entry by index.
+ *
+ * Flags to use when reading the phonebook.
+ */
+typedef enum {
+    MBIM_PHONEBOOK_FLAG_ALL   = 0,
+    MBIM_PHONEBOOK_FLAG_INDEX = 1
+} MbimPhonebookFlag;
+
+/**
+ * MbimPhonebookWriteFlag:
+ * @MBIM_PHONEBOOK_WRITE_FLAG_SAVE_UNUSED: Store the record in an unused slot.
+ * @MBIM_PHONEBOOK_WRITE_FLAG_SAVE_INDEX: Index where to store the record.
+ *
+ * Flags to use when writing the phonebook.
+ */
+typedef enum {
+    MBIM_PHONEBOOK_WRITE_FLAG_SAVE_UNUSED = 0,
+    MBIM_PHONEBOOK_WRITE_FLAG_SAVE_INDEX  = 1,
+} MbimPhonebookWriteFlag;
+
+/*****************************************************************************/
+/* 'STK PAC' enums */
+
+/**
+ * MbimStkPacProfile:
+ * @MBIM_STK_PAC_PROFILE_NOT_HANDLED_BY_FUNCTION_HANDLED_BY_HOST: Command not handled by function but handled by host.
+ * @MBIM_STK_PAC_PROFILE_NOT_HANDLED_BY_FUNCTION_MAY_BE_HANDLED_BY_HOST: Command not handled by function but may be handled by host.
+ * @MBIM_STK_PAC_PROFILE_HANDLED_BY_FUNCTION_ONLY_TRANSPARENT_TO_HOST: Command handled by function without informing the host.
+ * @MBIM_STK_PAC_PROFILE_HANDLED_BY_FUNCTION_NOTIFICATION_TO_HOST_POSSIBLE: Command handled by function without informing the host, but notifications may be sent to host.
+ * @MBIM_STK_PAC_PROFILE_HANDLED_BY_FUNCTION_NOTIFICATIONS_TO_HOST_ENABLED: Command handled by function, and the function wil also send notification to the host.
+ * @MBIM_STK_PAC_PROFILE_HANDLED_BY_FUNCTION_CAN_BE_OVERRIDEN_BY_HOST: Command handled by function, but the host may request full control of the command.
+ * @MBIM_STK_PAC_PROFILE_HANDLED_BY_HOST_FUNCTION_NOT_ABLE_TO_HANDLE: Command will be forwarded to the host. If the host decides not to receive the command, the function will not handle it.
+ * @MBIM_STK_PAC_PROFILE_HANDLED_BY_HOST_FUNCTION_ABLE_TO_HANDLE: Command will be forwarded to the host. If the host decides not to receive the command, the function will handle it.
+ *
+ * Proactive command profile.
+ */
+typedef enum {
+    MBIM_STK_PAC_PROFILE_NOT_HANDLED_BY_FUNCTION_HANDLED_BY_HOST           = 0,
+    MBIM_STK_PAC_PROFILE_NOT_HANDLED_BY_FUNCTION_MAY_BE_HANDLED_BY_HOST    = 1,
+    MBIM_STK_PAC_PROFILE_HANDLED_BY_FUNCTION_ONLY_TRANSPARENT_TO_HOST      = 2,
+    MBIM_STK_PAC_PROFILE_HANDLED_BY_FUNCTION_NOTIFICATION_TO_HOST_POSSIBLE = 3,
+    MBIM_STK_PAC_PROFILE_HANDLED_BY_FUNCTION_NOTIFICATIONS_TO_HOST_ENABLED = 4,
+    MBIM_STK_PAC_PROFILE_HANDLED_BY_FUNCTION_CAN_BE_OVERRIDEN_BY_HOST      = 5,
+    MBIM_STK_PAC_PROFILE_HANDLED_BY_HOST_FUNCTION_NOT_ABLE_TO_HANDLE       = 6,
+    MBIM_STK_PAC_PROFILE_HANDLED_BY_HOST_FUNCTION_ABLE_TO_HANDLE           = 7
+} MbimStkPacProfile;
+
+/**
+ * MbimStkPacType:
+ * @MBIM_STK_PAC_TYPE_PROACTIVE_COMMAND: Host is requested to handle the Proactive command.
+ * @MBIM_STK_PAC_TYPE_NOTIFICATION: Proactive command is handled by the function, but the host is notified.
+ *
+ * Type of proactive command.
+ */
+typedef enum {
+    MBIM_STK_PAC_TYPE_PROACTIVE_COMMAND = 0,
+    MBIM_STK_PAC_TYPE_NOTIFICATION      = 1
+} MbimStkPacType;
+
+/*****************************************************************************/
+/* 'Network idle hint' enums */
+
+/**
+ * MbimNetworkIdleHintState:
+ * @MBIM_NETWORK_IDLE_HINT_STATE_DISABLED: Disabled.
+ * @MBIM_NETWORK_IDLE_HINT_STATE_ENABLED: Enabled.
+ *
+ * Enable or disable network idle hint.
+ */
+typedef enum {
+    MBIM_NETWORK_IDLE_HINT_STATE_DISABLED = 0,
+    MBIM_NETWORK_IDLE_HINT_STATE_ENABLED  = 1
+} MbimNetworkIdleHintState;
+
+/*****************************************************************************/
+/* 'Emergency mode' enums */
+
+/**
+ * MbimEmergencyModeState:
+ * @MBIM_EMERGENCY_MODE_STATE_OFF: Off.
+ * @MBIM_EMERGENCY_MODE_STATE_ON: On.
+ *
+ * Emergency mode state.
+ */
+typedef enum {
+    MBIM_EMERGENCY_MODE_STATE_OFF = 0,
+    MBIM_EMERGENCY_MODE_STATE_ON  = 1
+} MbimEmergencyModeState;
+
+/*****************************************************************************/
+/* 'DSS connect' enums */
+
+/**
+ * MbimDssLinkState:
+ * @MBIM_DSS_LINK_STATE_DEACTIVATE: Deactivate.
+ * @MBIM_DSS_LINK_STATE_ACTIVATE: Activate.
+ *
+ * Action performed in the link state.
+ */
+typedef enum {
+    MBIM_DSS_LINK_STATE_DEACTIVATE = 0,
+    MBIM_DSS_LINK_STATE_ACTIVATE   = 1
+} MbimDssLinkState;
+
+G_END_DECLS
+
+#endif /* _LIBMBIM_GLIB_MBIM_ENUMS_H_ */
diff --git a/libmbim-glib/mbim-errors.h b/libmbim-glib/mbim-errors.h
new file mode 100644
index 0000000..644a534
--- /dev/null
+++ b/libmbim-glib/mbim-errors.h
@@ -0,0 +1,185 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2012 Aleksander Morgado <aleksander@lanedo.com>
+ */
+
+#ifndef _LIBMBIM_GLIB_MBIM_ERRORS_H_
+#define _LIBMBIM_GLIB_MBIM_ERRORS_H_
+
+#if !defined (__LIBMBIM_GLIB_H_INSIDE__) && !defined (LIBMBIM_GLIB_COMPILATION)
+#error "Only <libmbim-glib.h> can be included directly."
+#endif
+
+/**
+ * SECTION: mbim-errors
+ * @title: Errors
+ *
+ * This section defines common error types used in the interface.
+ */
+
+/* Prefixes for errors registered in DBus */
+#define MBIM_DBUS_ERROR_PREFIX          "org.freedesktop.libmbim.Error"
+#define MBIM_CORE_ERROR_DBUS_PREFIX     MBIM_DBUS_ERROR_PREFIX ".Core"
+#define MBIM_PROTOCOL_ERROR_DBUS_PREFIX MBIM_DBUS_ERROR_PREFIX ".Protocol"
+#define MBIM_STATUS_ERROR_DBUS_PREFIX   MBIM_DBUS_ERROR_PREFIX ".Status"
+
+/**
+ * MbimCoreError:
+ * @MBIM_CORE_ERROR_FAILED: Operation failed.
+ * @MBIM_CORE_ERROR_WRONG_STATE: Operation cannot be executed in the current state.
+ * @MBIM_CORE_ERROR_TIMEOUT: Operation timed out.
+ * @MBIM_CORE_ERROR_INVALID_ARGS: Invalid arguments given.
+ * @MBIM_CORE_ERROR_INVALID_MESSAGE: MBIM message is invalid.
+ * @MBIM_CORE_ERROR_UNSUPPORTED: Not supported.
+ * @MBIM_CORE_ERROR_ABORTED: Operation aborted..
+ *
+ * Common errors that may be reported by libmbim-glib.
+ */
+typedef enum { /*< underscore_name=mbim_core_error >*/
+    MBIM_CORE_ERROR_FAILED           = 0, /*< nick=Failed >*/
+    MBIM_CORE_ERROR_WRONG_STATE      = 1, /*< nick=WrongState >*/
+    MBIM_CORE_ERROR_TIMEOUT          = 2, /*< nick=Timeout >*/
+    MBIM_CORE_ERROR_INVALID_ARGS     = 3, /*< nick=InvalidArgs >*/
+    MBIM_CORE_ERROR_INVALID_MESSAGE  = 4, /*< nick=InvalidMessage >*/
+    MBIM_CORE_ERROR_UNSUPPORTED      = 5, /*< nick=Unsupported >*/
+    MBIM_CORE_ERROR_ABORTED          = 6  /*< nick=Aborted >*/
+} MbimCoreError;
+
+/**
+ * MbimProtocolError:
+ * @MBIM_PROTOCOL_ERROR_INVALID: Invalid MBIM error.
+ * @MBIM_PROTOCOL_ERROR_TIMEOUT_FRAGMENT: Timeout waiting for fragment.
+ * @MBIM_PROTOCOL_ERROR_FRAGMENT_OUT_OF_SEQUENCE: Fragment received out of sequence.
+ * @MBIM_PROTOCOL_ERROR_LENGTH_MISMATCH: Length mismatch.
+ * @MBIM_PROTOCOL_ERROR_DUPLICATED_TID: Duplicated transaction ID.
+ * @MBIM_PROTOCOL_ERROR_NOT_OPENED: Not opened.
+ * @MBIM_PROTOCOL_ERROR_UNKNOWN: Unknown error.
+ * @MBIM_PROTOCOL_ERROR_CANCEL: Cancel the operation.
+ * @MBIM_PROTOCOL_ERROR_MAX_TRANSFER: Maximum control transfer not supported.
+ *
+ * MBIM protocol errors.
+ */
+typedef enum {
+    MBIM_PROTOCOL_ERROR_INVALID                  = 0, /*< nick=Invalid >*/
+    MBIM_PROTOCOL_ERROR_TIMEOUT_FRAGMENT         = 1, /*< nick=TimeoutFragment >*/
+    MBIM_PROTOCOL_ERROR_FRAGMENT_OUT_OF_SEQUENCE = 2, /*< nick=FragmentOutOfSequence >*/
+    MBIM_PROTOCOL_ERROR_LENGTH_MISMATCH          = 3, /*< nick=LengthMismatch >*/
+    MBIM_PROTOCOL_ERROR_DUPLICATED_TID           = 4, /*< nick=DuplicatedTid >*/
+    MBIM_PROTOCOL_ERROR_NOT_OPENED               = 5, /*< nick=NotOpened >*/
+    MBIM_PROTOCOL_ERROR_UNKNOWN                  = 6, /*< nick=Unknown >*/
+    MBIM_PROTOCOL_ERROR_CANCEL                   = 7, /*< nick=Cancel >*/
+    MBIM_PROTOCOL_ERROR_MAX_TRANSFER             = 8  /*< nick=MaxTransfer >*/
+} MbimProtocolError;
+
+
+/**
+ * MbimStatusError:
+ * @MBIM_STATUS_ERROR_NONE: Success, no error.
+ * @MBIM_STATUS_ERROR_BUSY: Busy.
+ * @MBIM_STATUS_ERROR_FAILURE: Failure.
+ * @MBIM_STATUS_ERROR_SIM_NOT_INSERTED: SIM not inserted.
+ * @MBIM_STATUS_ERROR_BAD_SIM: Bad SIM.
+ * @MBIM_STATUS_ERROR_PIN_REQUIRED: PIN required.
+ * @MBIM_STATUS_ERROR_PIN_DISABLED: PIN disabled.
+ * @MBIM_STATUS_ERROR_NOT_REGISTERED: Not registered.
+ * @MBIM_STATUS_ERROR_PROVIDERS_NOT_FOUND: Providers not found.
+ * @MBIM_STATUS_ERROR_NO_DEVICE_SUPPORT: No device support.
+ * @MBIM_STATUS_ERROR_PROVIDER_NOT_VISIBLE: Provider not visible.
+ * @MBIM_STATUS_ERROR_DATA_CLASS_NOT_AVAILABLE: Data class not available.
+ * @MBIM_STATUS_ERROR_PACKET_SERVICE_DETACHED: Packet service detached.
+ * @MBIM_STATUS_ERROR_MAX_ACTIVATED_CONTEXTS: Max activated contexts.
+ * @MBIM_STATUS_ERROR_NOT_INITIALIZED: Not initialized.
+ * @MBIM_STATUS_ERROR_VOICE_CALL_IN_PROGRESS: Voice call in progress.
+ * @MBIM_STATUS_ERROR_CONTEXT_NOT_ACTIVATED: Context not activated.
+ * @MBIM_STATUS_ERROR_SERVICE_NOT_ACTIVATED: Service not activated.
+ * @MBIM_STATUS_ERROR_INVALID_ACCESS_STRING: Invalid access string.
+ * @MBIM_STATUS_ERROR_INVALID_USER_NAME_PWD: Invalid user name or password.
+ * @MBIM_STATUS_ERROR_RADIO_POWER_OFF: Radio power off.
+ * @MBIM_STATUS_ERROR_INVALID_PARAMETERS: Invalid parameters.
+ * @MBIM_STATUS_ERROR_READ_FAILURE: Read failure.
+ * @MBIM_STATUS_ERROR_WRITE_FAILURE: Write failure.
+ * @MBIM_STATUS_ERROR_NO_PHONEBOOK: No phonebook.
+ * @MBIM_STATUS_ERROR_PARAMETER_TOO_LONG: Parameter too long.
+ * @MBIM_STATUS_ERROR_STK_BUSY: SIM toolkit busy.
+ * @MBIM_STATUS_ERROR_OPERATION_NOT_ALLOWED: Operation not allowed.
+ * @MBIM_STATUS_ERROR_MEMORY_FAILURE: Memory failure.
+ * @MBIM_STATUS_ERROR_INVALID_MEMORY_INDEX: Invalid memory index.
+ * @MBIM_STATUS_ERROR_MEMORY_FULL: Memory full.
+ * @MBIM_STATUS_ERROR_FILTER_NOT_SUPPORTED: Filter not supported.
+ * @MBIM_STATUS_ERROR_DSS_INSTANCE_LIMIT: DSS instance limit.
+ * @MBIM_STATUS_ERROR_INVALID_DEVICE_SERVICE_OPERATION: Invalid device service operation.
+ * @MBIM_STATUS_ERROR_AUTH_INCORRECT_AUTN: Incorrect AUTN when sending authentication.
+ * @MBIM_STATUS_ERROR_AUTH_SYNC_FAILURE: Synchronization failure during the authentication.
+ * @MBIM_STATUS_ERROR_AUTH_AMF_NOT_SET: AMF bit not set in the authentication.
+ * @MBIM_STATUS_ERROR_SMS_UNKNOWN_SMSC_ADDRESS: Unknown SMSC address.
+ * @MBIM_STATUS_ERROR_SMS_NETWORK_TIMEOUT: Network timeout when sending SMS.
+ * @MBIM_STATUS_ERROR_SMS_LANG_NOT_SUPPORTED: Language not supported in SMS.
+ * @MBIM_STATUS_ERROR_SMS_ENCODING_NOT_SUPPORTED: Encoding not supported in SMS.
+ * @MBIM_STATUS_ERROR_SMS_FORMAT_NOT_SUPPORTED: Format not supported in SMS.
+ *
+ * Status of the MBIM request.
+ */
+typedef enum {
+    MBIM_STATUS_ERROR_NONE                             = 0,  /*< nick=None >*/
+    MBIM_STATUS_ERROR_BUSY                             = 1,  /*< nick=Busy >*/
+    MBIM_STATUS_ERROR_FAILURE                          = 2,  /*< nick=Failure >*/
+    MBIM_STATUS_ERROR_SIM_NOT_INSERTED                 = 3,  /*< nick=SimNotInserted >*/
+    MBIM_STATUS_ERROR_BAD_SIM                          = 4,  /*< nick=BadSim >*/
+    MBIM_STATUS_ERROR_PIN_REQUIRED                     = 5,  /*< nick=PinRequired >*/
+    MBIM_STATUS_ERROR_PIN_DISABLED                     = 6,  /*< nick=PinDisabled >*/
+    MBIM_STATUS_ERROR_NOT_REGISTERED                   = 7,  /*< nick=NotRegistered >*/
+    MBIM_STATUS_ERROR_PROVIDERS_NOT_FOUND              = 8,  /*< nick=ProvidersNotFound >*/
+    MBIM_STATUS_ERROR_NO_DEVICE_SUPPORT                = 9,  /*< nick=NoDeviceSupport >*/
+    MBIM_STATUS_ERROR_PROVIDER_NOT_VISIBLE             = 10, /*< nick=ProviderNotVisible >*/
+    MBIM_STATUS_ERROR_DATA_CLASS_NOT_AVAILABLE         = 11, /*< nick=DataClassNotAvailable >*/
+    MBIM_STATUS_ERROR_PACKET_SERVICE_DETACHED          = 12, /*< nick=PacketServiceDetached >*/
+    MBIM_STATUS_ERROR_MAX_ACTIVATED_CONTEXTS           = 13, /*< nick=MaxActivatedContexts >*/
+    MBIM_STATUS_ERROR_NOT_INITIALIZED                  = 14, /*< nick=NotInitialized >*/
+    MBIM_STATUS_ERROR_VOICE_CALL_IN_PROGRESS           = 15, /*< nick=VoiceCallInProgress >*/
+    MBIM_STATUS_ERROR_CONTEXT_NOT_ACTIVATED            = 16, /*< nick=ContextNotActivated >*/
+    MBIM_STATUS_ERROR_SERVICE_NOT_ACTIVATED            = 17, /*< nick=ServiceNotActivated >*/
+    MBIM_STATUS_ERROR_INVALID_ACCESS_STRING            = 18, /*< nick=InvalidAccessString >*/
+    MBIM_STATUS_ERROR_INVALID_USER_NAME_PWD            = 19, /*< nick=InvalidUserNamePwd >*/
+    MBIM_STATUS_ERROR_RADIO_POWER_OFF                  = 20, /*< nick=RadioPowerOff >*/
+    MBIM_STATUS_ERROR_INVALID_PARAMETERS               = 21, /*< nick=InvalidParameters >*/
+    MBIM_STATUS_ERROR_READ_FAILURE                     = 22, /*< nick=ReadFailure >*/
+    MBIM_STATUS_ERROR_WRITE_FAILURE                    = 23, /*< nick=WriteFailure >*/
+    /* 24 = reserved */
+    MBIM_STATUS_ERROR_NO_PHONEBOOK                     = 25, /*< nick=NoPhonebook >*/
+    MBIM_STATUS_ERROR_PARAMETER_TOO_LONG               = 26, /*< nick=ParameterTooLong >*/
+    MBIM_STATUS_ERROR_STK_BUSY                         = 27, /*< nick=StkBusy >*/
+    MBIM_STATUS_ERROR_OPERATION_NOT_ALLOWED            = 28, /*< nick=OperationNotAllowed >*/
+    MBIM_STATUS_ERROR_MEMORY_FAILURE                   = 29, /*< nick=MemoryFailure >*/
+    MBIM_STATUS_ERROR_INVALID_MEMORY_INDEX             = 30, /*< nick=InvalidMemoryIndex >*/
+    MBIM_STATUS_ERROR_MEMORY_FULL                      = 31, /*< nick=MemoryFull >*/
+    MBIM_STATUS_ERROR_FILTER_NOT_SUPPORTED             = 32, /*< nick=FilterNotSupported >*/
+    MBIM_STATUS_ERROR_DSS_INSTANCE_LIMIT               = 33, /*< nick=DssInstanceLimit >*/
+    MBIM_STATUS_ERROR_INVALID_DEVICE_SERVICE_OPERATION = 34, /*< nick=InvalidDeviceServiceOperation >*/
+    MBIM_STATUS_ERROR_AUTH_INCORRECT_AUTN              = 35, /*< nick=AuthIncorrectAuth >*/
+    MBIM_STATUS_ERROR_AUTH_SYNC_FAILURE                = 36, /*< nick=AuthSyncFailure >*/
+    MBIM_STATUS_ERROR_AUTH_AMF_NOT_SET                 = 37, /*< nick=AuthAmfNotSet >*/
+    MBIM_STATUS_ERROR_SMS_UNKNOWN_SMSC_ADDRESS         = 100, /*< nick=SmsUnknownSmscAddress >*/
+    MBIM_STATUS_ERROR_SMS_NETWORK_TIMEOUT              = 101, /*< nick=SmsNetworkTimeout >*/
+    MBIM_STATUS_ERROR_SMS_LANG_NOT_SUPPORTED           = 102, /*< nick=SmsLangNotSupported >*/
+    MBIM_STATUS_ERROR_SMS_ENCODING_NOT_SUPPORTED       = 103, /*< nick=SmsEncodingNotSupported >*/
+    MBIM_STATUS_ERROR_SMS_FORMAT_NOT_SUPPORTED         = 104  /*< nick=SmsFormatNotSupported >*/
+} MbimStatusError;
+
+#endif /* _LIBMBIM_GLIB_MBIM_ERRORS_H_ */
diff --git a/libmbim-glib/mbim-message-private.h b/libmbim-glib/mbim-message-private.h
new file mode 100644
index 0000000..5d9d509
--- /dev/null
+++ b/libmbim-glib/mbim-message-private.h
@@ -0,0 +1,296 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ *
+ * This is a private non-installed header
+ */
+
+#ifndef _LIBMBIM_GLIB_MBIM_MESSAGE_PRIVATE_H_
+#define _LIBMBIM_GLIB_MBIM_MESSAGE_PRIVATE_H_
+
+#if !defined (LIBMBIM_GLIB_COMPILATION)
+#error "This is a private header!!"
+#endif
+
+#include <glib.h>
+
+#include "mbim-message.h"
+
+G_BEGIN_DECLS
+
+/*****************************************************************************/
+/* The MbimMessage */
+
+/* Defined in the same way as GByteArray */
+struct _MbimMessage {
+  guint8 *data;
+  guint	  len;
+};
+
+/*****************************************************************************/
+/* Basic message types */
+
+struct header {
+  guint32 type;
+  guint32 length;
+  guint32 transaction_id;
+} __attribute__((packed));
+
+#define MBIM_MESSAGE_GET_MESSAGE_TYPE(self)                             \
+    (MbimMessageType) GUINT32_FROM_LE (((struct header *)(self->data))->type)
+#define MBIM_MESSAGE_GET_MESSAGE_LENGTH(self)                           \
+    GUINT32_FROM_LE (((struct header *)(self->data))->length)
+#define MBIM_MESSAGE_GET_TRANSACTION_ID(self)                           \
+    GUINT32_FROM_LE (((struct header *)(self->data))->transaction_id)
+
+struct open_message {
+    guint32 max_control_transfer;
+} __attribute__((packed));
+
+struct open_done_message {
+    guint32 status_code;
+} __attribute__((packed));
+
+struct close_done_message {
+    guint32 status_code;
+} __attribute__((packed));
+
+struct error_message {
+    guint32 error_status_code;
+} __attribute__((packed));
+
+struct fragment_header {
+  guint32 total;
+  guint32 current;
+} __attribute__((packed));
+
+struct fragment_message {
+    struct fragment_header fragment_header;
+    guint8                 buffer[];
+} __attribute__((packed));
+
+struct command_message {
+    struct fragment_header fragment_header;
+    guint8                 service_id[16];
+    guint32                command_id;
+    guint32                command_type;
+    guint32                buffer_length;
+    guint8                 buffer[];
+} __attribute__((packed));
+
+struct command_done_message {
+    struct fragment_header fragment_header;
+    guint8                 service_id[16];
+    guint32                command_id;
+    guint32                status_code;
+    guint32                buffer_length;
+    guint8                 buffer[];
+} __attribute__((packed));
+
+struct indicate_status_message {
+    struct fragment_header fragment_header;
+    guint8                 service_id[16];
+    guint32                command_id;
+    guint32                buffer_length;
+    guint8                 buffer[];
+} __attribute__((packed));
+
+struct full_message {
+    struct header header;
+    union {
+        struct open_message            open;
+        struct open_done_message       open_done;
+        /* nothing needed for close_message */
+        struct close_done_message      close_done;
+        struct error_message           error;
+        struct fragment_message        fragment;
+        struct command_message         command;
+        struct command_done_message    command_done;
+        struct indicate_status_message indicate_status;
+    } message;
+} __attribute__((packed));
+
+/*****************************************************************************/
+/* Fragment interface */
+
+gboolean      _mbim_message_is_fragment          (const MbimMessage  *self);
+guint32       _mbim_message_fragment_get_total   (const MbimMessage  *self);
+guint32       _mbim_message_fragment_get_current (const MbimMessage  *self);
+const guint8 *_mbim_message_fragment_get_payload (const MbimMessage  *self,
+                                                  guint32            *length);
+
+/* Merge fragments into a message... */
+
+MbimMessage *_mbim_message_fragment_collector_init     (const MbimMessage  *fragment,
+                                                        GError            **error);
+gboolean     _mbim_message_fragment_collector_add      (MbimMessage        *self,
+                                                        const MbimMessage  *fragment,
+                                                        GError            **error);
+gboolean     _mbim_message_fragment_collector_complete (MbimMessage        *self);
+
+/* Split message into fragments... */
+
+struct fragment_info {
+    struct header           header;
+    struct fragment_header  fragment_header;
+    guint32                 data_length;
+    const guint8           *data;
+} __attribute__((packed));
+
+struct fragment_info *_mbim_message_split_fragments (const MbimMessage *self,
+                                                     guint32            max_fragment_size,
+                                                     guint             *n_fragments);
+
+/*****************************************************************************/
+/* Struct builder */
+
+typedef struct {
+    GByteArray  *fixed_buffer;
+    GByteArray  *variable_buffer;
+    GArray      *offsets;
+} MbimStructBuilder;
+
+MbimStructBuilder *_mbim_struct_builder_new                  (void);
+GByteArray        *_mbim_struct_builder_complete             (MbimStructBuilder *builder);
+void               _mbim_struct_builder_append_byte_array    (MbimStructBuilder *builder,
+                                                              gboolean           with_offset,
+                                                              gboolean           with_length,
+                                                              const guint8      *buffer,
+                                                              guint32            buffer_len);
+void               _mbim_struct_builder_append_uuid          (MbimStructBuilder *builder,
+                                                              const MbimUuid    *value);
+void               _mbim_struct_builder_append_guint32       (MbimStructBuilder *builder,
+                                                              guint32            value);
+void               _mbim_struct_builder_append_guint32_array (MbimStructBuilder *builder,
+                                                              const guint32     *values,
+                                                              guint32            n_values);
+void               _mbim_struct_builder_append_guint64       (MbimStructBuilder *builder,
+                                                              guint64            value);
+void               _mbim_struct_builder_append_guint64_array (MbimStructBuilder *builder,
+                                                              const guint64     *values,
+                                                              guint32            n_values);
+void               _mbim_struct_builder_append_string        (MbimStructBuilder *builder,
+                                                              const gchar       *value);
+void               _mbim_struct_builder_append_string_array  (MbimStructBuilder  *builder,
+                                                              const gchar *const *values,
+                                                              guint32             n_values);
+void               _mbim_struct_builder_append_ipv4          (MbimStructBuilder *builder,
+                                                              const MbimIPv4    *value,
+                                                              gboolean           ref);
+void               _mbim_struct_builder_append_ipv4_array    (MbimStructBuilder *builder,
+                                                              const MbimIPv4    *values,
+                                                              guint32            n_values);
+void               _mbim_struct_builder_append_ipv6          (MbimStructBuilder *builder,
+                                                              const MbimIPv6    *value,
+                                                              gboolean           ref);
+void               _mbim_struct_builder_append_ipv6_array    (MbimStructBuilder *builder,
+                                                              const MbimIPv6    *values,
+                                                              guint32            n_values);
+
+/*****************************************************************************/
+/* Message builder */
+
+typedef struct {
+    MbimMessage *message;
+    MbimStructBuilder *contents_builder;
+} MbimMessageCommandBuilder;
+
+MbimMessageCommandBuilder *_mbim_message_command_builder_new                  (guint32                    transaction_id,
+                                                                               MbimService                service,
+                                                                               guint32                    cid,
+                                                                               MbimMessageCommandType     command_type);
+MbimMessage               *_mbim_message_command_builder_complete             (MbimMessageCommandBuilder *builder);
+void                       _mbim_message_command_builder_append_byte_array    (MbimMessageCommandBuilder *builder,
+                                                                               gboolean                   with_offset,
+                                                                               gboolean                   with_length,
+                                                                               const guint8              *buffer,
+                                                                               guint32                    buffer_len);
+void                       _mbim_message_command_builder_append_uuid          (MbimMessageCommandBuilder *builder,
+                                                                               const MbimUuid            *value);
+void                       _mbim_message_command_builder_append_guint32       (MbimMessageCommandBuilder *builder,
+                                                                               guint32                    value);
+void                       _mbim_message_command_builder_append_guint32_array (MbimMessageCommandBuilder *builder,
+                                                                               const guint32             *values,
+                                                                               guint32                    n_values);
+void                       _mbim_message_command_builder_append_guint64       (MbimMessageCommandBuilder *builder,
+                                                                               guint64                    value);
+void                       _mbim_message_command_builder_append_guint64_array (MbimMessageCommandBuilder *builder,
+                                                                               const guint64             *values,
+                                                                               guint32                    n_values);
+void                       _mbim_message_command_builder_append_string        (MbimMessageCommandBuilder *builder,
+                                                                               const gchar               *value);
+void                       _mbim_message_command_builder_append_string_array  (MbimMessageCommandBuilder *builder,
+                                                                               const gchar *const        *values,
+                                                                               guint32                    n_values);
+void                       _mbim_message_command_builder_append_ipv4          (MbimMessageCommandBuilder *builder,
+                                                                               const MbimIPv4            *value,
+                                                                               gboolean                   ref);
+void                       _mbim_message_command_builder_append_ipv4_array    (MbimMessageCommandBuilder *builder,
+                                                                               const MbimIPv4            *values,
+                                                                               guint32                    n_values);
+void                       _mbim_message_command_builder_append_ipv6          (MbimMessageCommandBuilder *builder,
+                                                                               const MbimIPv6            *value,
+                                                                               gboolean                   ref);
+void                       _mbim_message_command_builder_append_ipv6_array    (MbimMessageCommandBuilder *builder,
+                                                                               const MbimIPv6            *values,
+                                                                               guint32                    n_values);
+
+/*****************************************************************************/
+/* Message parser */
+
+const guint8    *_mbim_message_read_byte_array    (const MbimMessage *self,
+                                                   guint32            struct_start_offset,
+                                                   guint32            relative_offset,
+                                                   gboolean           has_offset,
+                                                   gboolean           has_length,
+                                                   guint32           *array_size);
+const MbimUuid  *_mbim_message_read_uuid          (const MbimMessage *self,
+                                                   guint32            relative_offset);
+guint32          _mbim_message_read_guint32       (const MbimMessage *self,
+                                                   guint32            relative_offset);
+guint32         *_mbim_message_read_guint32_array (const MbimMessage *self,
+                                                   guint32            array_size,
+                                                   guint32            relative_offset_array_start);
+guint64          _mbim_message_read_guint64       (const MbimMessage *self,
+                                                   guint64            relative_offset);
+gchar           *_mbim_message_read_string        (const MbimMessage *self,
+                                                   guint32            struct_start_offset,
+                                                   guint32            relative_offset);
+gchar          **_mbim_message_read_string_array  (const MbimMessage *self,
+                                                   guint32            array_size,
+                                                   guint32            struct_start_offset,
+                                                   guint32            relative_offset_array_start);
+const MbimIPv4  *_mbim_message_read_ipv4          (const MbimMessage *self,
+                                                   guint32            relative_offset,
+                                                   gboolean           ref);
+MbimIPv4        *_mbim_message_read_ipv4_array    (const MbimMessage *self,
+                                                   guint32            array_size,
+                                                   guint32            relative_offset_array_start);
+const MbimIPv6  *_mbim_message_read_ipv6          (const MbimMessage *self,
+                                                   guint32            relative_offset,
+                                                   gboolean           ref);
+MbimIPv6        *_mbim_message_read_ipv6_array    (const MbimMessage *self,
+                                                   guint32            array_size,
+                                                   guint32            relative_offset_array_start);
+
+G_END_DECLS
+
+#endif /* _LIBMBIM_GLIB_MBIM_MESSAGE_PRIVATE_H_ */
diff --git a/libmbim-glib/mbim-message.c b/libmbim-glib/mbim-message.c
new file mode 100644
index 0000000..deb2c52
--- /dev/null
+++ b/libmbim-glib/mbim-message.c
@@ -0,0 +1,2075 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include <glib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <endian.h>
+
+#include "mbim-message.h"
+#include "mbim-message-private.h"
+#include "mbim-error-types.h"
+#include "mbim-enum-types.h"
+
+/**
+ * SECTION:mbim-message
+ * @title: MbimMessage
+ * @short_description: Generic MBIM message handling routines
+ *
+ * #MbimMessage is a generic type representing a MBIM message of any kind
+ * (request, response, indication).
+ **/
+
+/*****************************************************************************/
+
+GType
+mbim_message_get_type (void)
+{
+    static volatile gsize g_define_type_id__volatile = 0;
+
+    if (g_once_init_enter (&g_define_type_id__volatile)) {
+        GType g_define_type_id =
+            g_boxed_type_register_static (g_intern_static_string ("MbimMessage"),
+                                          (GBoxedCopyFunc) mbim_message_ref,
+                                          (GBoxedFreeFunc) mbim_message_unref);
+
+        g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+    }
+
+    return g_define_type_id__volatile;
+}
+
+/*****************************************************************************/
+
+static GByteArray *
+_mbim_message_allocate (MbimMessageType message_type,
+                        guint32         transaction_id,
+                        guint32         additional_size)
+{
+    GByteArray *self;
+    guint32 len;
+
+    /* Compute size of the basic empty message and allocate heap for it */
+    len = sizeof (struct header) + additional_size;
+    self = g_byte_array_sized_new (len);
+    g_byte_array_set_size (self, len);
+
+    /* Set MBIM header */
+    ((struct header *)(self->data))->type           = GUINT32_TO_LE (message_type);
+    ((struct header *)(self->data))->length         = GUINT32_TO_LE (len);
+    ((struct header *)(self->data))->transaction_id = GUINT32_TO_LE (transaction_id);
+
+    return self;
+}
+
+static guint32
+_mbim_message_get_information_buffer_offset (const MbimMessage *self)
+{
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND ||
+                          MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND_DONE ||
+                          MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_INDICATE_STATUS, 0);
+
+    switch (MBIM_MESSAGE_GET_MESSAGE_TYPE (self)) {
+    case MBIM_MESSAGE_TYPE_COMMAND:
+        return (sizeof (struct header) +
+                G_STRUCT_OFFSET (struct command_message, buffer));
+
+    case MBIM_MESSAGE_TYPE_COMMAND_DONE:
+        return (sizeof (struct header) +
+                G_STRUCT_OFFSET (struct command_done_message, buffer));
+        break;
+
+    case MBIM_MESSAGE_TYPE_INDICATE_STATUS:
+        return (sizeof (struct header) +
+                G_STRUCT_OFFSET (struct indicate_status_message, buffer));
+        break;
+
+    default:
+        g_assert_not_reached ();
+        return 0;
+    }
+}
+
+guint32
+_mbim_message_read_guint32 (const MbimMessage *self,
+                            guint32            relative_offset)
+{
+    guint32 information_buffer_offset;
+
+    information_buffer_offset = _mbim_message_get_information_buffer_offset (self);
+    return GUINT32_FROM_LE (G_STRUCT_MEMBER (
+                                guint32,
+                                self->data,
+                                (information_buffer_offset + relative_offset)));
+}
+
+guint32 *
+_mbim_message_read_guint32_array (const MbimMessage *self,
+                                  guint32            array_size,
+                                  guint32            relative_offset_array_start)
+{
+    guint i;
+    guint32 *out;
+    guint32 information_buffer_offset;
+
+    if (!array_size)
+        return NULL;
+
+    information_buffer_offset = _mbim_message_get_information_buffer_offset (self);
+
+    out = g_new (guint32, array_size + 1);
+    for (i = 0; i < array_size; i++) {
+        out[i] = GUINT32_FROM_LE (G_STRUCT_MEMBER (
+                                      guint32,
+                                      self->data,
+                                      (information_buffer_offset +
+                                       relative_offset_array_start +
+                                       (4 * i))));
+    }
+    out[array_size] = 0;
+
+    return out;
+}
+
+guint64
+_mbim_message_read_guint64 (const MbimMessage *self,
+                            guint64            relative_offset)
+{
+    guint64 information_buffer_offset;
+
+    information_buffer_offset = _mbim_message_get_information_buffer_offset (self);
+    return GUINT64_FROM_LE (G_STRUCT_MEMBER (
+                                guint64,
+                                self->data,
+                                (information_buffer_offset + relative_offset)));
+}
+
+gchar *
+_mbim_message_read_string (const MbimMessage *self,
+                           guint32            struct_start_offset,
+                           guint32            relative_offset)
+{
+    guint32 offset;
+    guint32 size;
+    gchar *str;
+    GError *error = NULL;
+    guint32 information_buffer_offset;
+
+    information_buffer_offset = _mbim_message_get_information_buffer_offset (self);
+
+    offset = GUINT32_FROM_LE (G_STRUCT_MEMBER (
+                                  guint32,
+                                  self->data,
+                                  (information_buffer_offset + relative_offset)));
+    size   = GUINT32_FROM_LE (G_STRUCT_MEMBER (
+                                  guint32,
+                                  self->data,
+                                  (information_buffer_offset + relative_offset + 4)));
+    if (!size)
+        return NULL;
+
+    str = g_convert (G_STRUCT_MEMBER_P (
+                         self->data,
+                         (information_buffer_offset + struct_start_offset + offset)),
+                     size,
+                     "utf-8",
+                     "utf-16le",
+                     NULL,
+                     NULL,
+                     &error);
+    if (error) {
+        g_warning ("Error converting string: %s", error->message);
+        g_error_free (error);
+    }
+
+    return str;
+}
+
+gchar **
+_mbim_message_read_string_array (const MbimMessage *self,
+                                 guint32            array_size,
+                                 guint32            struct_start_offset,
+                                 guint32            relative_offset_array_start)
+{
+    gchar **array;
+    guint32 offset;
+    guint32 i;
+
+    if (!array_size)
+        return NULL;
+
+    array = g_new (gchar *, array_size + 1);
+    for (i = 0, offset = relative_offset_array_start;
+         i < array_size;
+         offset += 8, i++) {
+        /* Read next string in the OL pair list */
+        array[i] = _mbim_message_read_string (self, struct_start_offset, offset);
+    }
+    array[i] = NULL;
+
+    return array;
+}
+
+/*
+ * Byte arrays may be given in very different ways:
+ *  - (a) Offset + Length pair in static buffer, data in variable buffer.
+ *  - (b) Just length in static buffer, data just afterwards.
+ *  - (c) Just offset in static buffer, length given in another variable, data in variable buffer.
+ *  - (d) Fixed-sized array directly in the static buffer.
+ *  - (e) Unsized array directly in the variable buffer, length is assumed until end of message.
+ */
+const guint8 *
+_mbim_message_read_byte_array (const MbimMessage *self,
+                               guint32            struct_start_offset,
+                               guint32            relative_offset,
+                               gboolean           has_offset,
+                               gboolean           has_length,
+                               guint32           *array_size)
+{
+    guint32 information_buffer_offset;
+
+    information_buffer_offset = _mbim_message_get_information_buffer_offset (self);
+
+    /* (a) Offset + Length pair in static buffer, data in variable buffer. */
+    if (has_offset && has_length) {
+        guint32 offset;
+
+        g_assert (array_size != NULL);
+
+        offset = GUINT32_FROM_LE (G_STRUCT_MEMBER (
+                                      guint32,
+                                      self->data,
+                                      (information_buffer_offset + relative_offset)));
+        *array_size = GUINT32_FROM_LE (G_STRUCT_MEMBER (
+                                           guint32,
+                                           self->data,
+                                           (information_buffer_offset + relative_offset + 4)));
+        return (const guint8 *) G_STRUCT_MEMBER_P (self->data,
+                                                   (information_buffer_offset + struct_start_offset + offset));
+    }
+
+    /* (b) Just length in static buffer, data just afterwards. */
+    if (!has_offset && has_length) {
+        g_assert (array_size != NULL);
+
+        *array_size = GUINT32_FROM_LE (G_STRUCT_MEMBER (
+                                           guint32,
+                                           self->data,
+                                           (information_buffer_offset + relative_offset)));
+        return (const guint8 *) G_STRUCT_MEMBER_P (self->data,
+                                                   (information_buffer_offset + relative_offset + 4));
+    }
+
+    /* (c) Just offset in static buffer, length given in another variable, data in variable buffer. */
+    if (has_offset && !has_length) {
+        guint32 offset;
+
+        g_assert (array_size == NULL);
+
+        offset = GUINT32_FROM_LE (G_STRUCT_MEMBER (
+                                      guint32,
+                                      self->data,
+                                      (information_buffer_offset + relative_offset)));
+        return (const guint8 *) G_STRUCT_MEMBER_P (self->data,
+                                                   (information_buffer_offset + struct_start_offset + offset));
+    }
+
+    /* (d) Fixed-sized array directly in the static buffer.
+     * (e) Unsized array directly in the variable buffer, length is assumed until end of message. */
+    if (!has_offset && !has_length) {
+        /* If array size is requested, it's case (e) */
+        if (array_size)
+            *array_size = self->len - (information_buffer_offset + relative_offset);
+
+        return (const guint8 *) G_STRUCT_MEMBER_P (self->data,
+                                                   (information_buffer_offset + relative_offset));
+    }
+
+    g_assert_not_reached ();
+}
+
+const MbimUuid *
+_mbim_message_read_uuid (const MbimMessage *self,
+                         guint32            relative_offset)
+{
+    guint32 information_buffer_offset;
+
+    information_buffer_offset = _mbim_message_get_information_buffer_offset (self);
+
+    return (const MbimUuid *) G_STRUCT_MEMBER_P (self->data,
+                                                 (information_buffer_offset + relative_offset));
+}
+
+const MbimIPv4 *
+_mbim_message_read_ipv4 (const MbimMessage *self,
+                         guint32            relative_offset,
+                         gboolean           ref)
+{
+    guint32 information_buffer_offset;
+    guint32 offset;
+
+    information_buffer_offset = _mbim_message_get_information_buffer_offset (self);
+
+    if (ref) {
+        offset = GUINT32_FROM_LE (G_STRUCT_MEMBER (guint32,
+                                                   self->data,
+                                                   (information_buffer_offset + relative_offset)));
+        if (!offset)
+            return NULL;
+    } else
+        offset = relative_offset;
+
+    return (const MbimIPv4 *) G_STRUCT_MEMBER_P (self->data,
+                                                 (information_buffer_offset + offset));
+}
+
+MbimIPv4 *
+_mbim_message_read_ipv4_array (const MbimMessage *self,
+                               guint32            array_size,
+                               guint32            relative_offset_array_start)
+{
+    MbimIPv4 *array;
+    guint32 offset;
+    guint32 i;
+    guint32 information_buffer_offset;
+
+    if (!array_size)
+        return NULL;
+
+    information_buffer_offset = _mbim_message_get_information_buffer_offset (self);
+
+    array = g_new (MbimIPv4, array_size);
+    offset = GUINT32_FROM_LE (G_STRUCT_MEMBER (
+                                  guint32,
+                                  self->data,
+                                  (information_buffer_offset + relative_offset_array_start)));
+
+    for (i = 0; i < array_size; i++, offset += 4) {
+        memcpy (&array[i],
+                G_STRUCT_MEMBER_P (self->data,
+                                   (information_buffer_offset + offset)),
+                4);
+    }
+
+    return array;
+}
+
+const MbimIPv6 *
+_mbim_message_read_ipv6 (const MbimMessage *self,
+                         guint32            relative_offset,
+                         gboolean           ref)
+{
+    guint32 information_buffer_offset;
+    guint32 offset;
+
+    information_buffer_offset = _mbim_message_get_information_buffer_offset (self);
+
+    if (ref) {
+        offset = GUINT32_FROM_LE (G_STRUCT_MEMBER (guint32,
+                                                   self->data,
+                                                   (information_buffer_offset + relative_offset)));
+        if (!offset)
+            return NULL;
+    } else
+        offset = relative_offset;
+
+    return (const MbimIPv6 *) G_STRUCT_MEMBER_P (self->data,
+                                                 (information_buffer_offset + offset));
+}
+
+MbimIPv6 *
+_mbim_message_read_ipv6_array (const MbimMessage *self,
+                               guint32            array_size,
+                               guint32            relative_offset_array_start)
+{
+    MbimIPv6 *array;
+    guint32 offset;
+    guint32 i;
+    guint32 information_buffer_offset;
+
+    if (!array_size)
+        return NULL;
+
+    information_buffer_offset = _mbim_message_get_information_buffer_offset (self);
+
+    array = g_new (MbimIPv6, array_size);
+    offset = GUINT32_FROM_LE (G_STRUCT_MEMBER (
+                                  guint32,
+                                  self->data,
+                                  (information_buffer_offset + relative_offset_array_start)));
+    for (i = 0; i < array_size; i++, offset += 16) {
+        memcpy (&array[i],
+                G_STRUCT_MEMBER_P (self->data,
+                                   (information_buffer_offset + offset)),
+                16);
+    }
+
+    return array;
+}
+
+/*****************************************************************************/
+/* Struct builder interface
+ *
+ * Types like structs consist of a fixed sized prefix plus a variable length
+ * data buffer. Items of variable size are usually given as an offset (with
+ * respect to the start of the struct) plus a size field. */
+
+MbimStructBuilder *
+_mbim_struct_builder_new (void)
+{
+    MbimStructBuilder *builder;
+
+    builder = g_slice_new (MbimStructBuilder);
+    builder->fixed_buffer = g_byte_array_new ();
+    builder->variable_buffer = g_byte_array_new ();
+    builder->offsets = g_array_new (FALSE, FALSE, sizeof (guint32));
+    return builder;
+}
+
+GByteArray *
+_mbim_struct_builder_complete (MbimStructBuilder *builder)
+{
+    GByteArray *out;
+    guint i;
+
+    /* Update offsets with the length of the information buffer, and store them
+     * in LE. */
+    for (i = 0; i < builder->offsets->len; i++) {
+        guint32 offset_offset;
+        guint32 *offset_value;
+
+        offset_offset = g_array_index (builder->offsets, guint32, i);
+        offset_value = (guint32 *) &builder->fixed_buffer->data[offset_offset];
+        *offset_value = GUINT32_TO_LE (*offset_value + builder->fixed_buffer->len);
+    }
+
+    /* Merge both buffers */
+    g_byte_array_append (builder->fixed_buffer,
+                         (const guint8 *)builder->variable_buffer->data,
+                         (guint32)builder->variable_buffer->len);
+
+    /* Steal the buffer to return */
+    out = builder->fixed_buffer;
+
+    /* Dispose the builder */
+    g_array_unref (builder->offsets);
+    g_byte_array_unref (builder->variable_buffer);
+    g_slice_free (MbimStructBuilder, builder);
+
+    return out;
+}
+
+/*
+ * Byte arrays may be given in very different ways:
+ *  - (a) Offset + Length pair in static buffer, data in variable buffer.
+ *  - (b) Just length in static buffer, data just afterwards.
+ *  - (c) Just offset in static buffer, length given in another variable, data in variable buffer.
+ *  - (d) Fixed-sized array directly in the static buffer.
+ *  - (e) Unsized array directly in the variable buffer, length is assumed until end of message.
+ */
+void
+_mbim_struct_builder_append_byte_array (MbimStructBuilder *builder,
+                                        gboolean           with_offset,
+                                        gboolean           with_length,
+                                        const guint8      *buffer,
+                                        guint32            buffer_len)
+{
+    /*
+     * (d) Fixed-sized array directly in the static buffer.
+     * (e) Unsized array directly in the variable buffer (here end of static buffer is also beginning of variable)
+     */
+    if (!with_offset && !with_length) {
+        g_byte_array_append (builder->fixed_buffer, buffer, buffer_len);
+        while (buffer_len % 4 != 0) {
+            const guint8 padding = 0;
+
+            g_byte_array_append (builder->fixed_buffer, &padding, 1);
+            buffer_len++;
+        }
+        return;
+    }
+
+    /* (a) Offset + Length pair in static buffer, data in variable buffer.
+     * This case is the sum of cases b+c */
+
+    /* (c) Just offset in static buffer, length given in another variable, data in variable buffer. */
+    if (with_offset) {
+        guint32 offset;
+
+        /* If string length is greater than 0, add the offset to fix, otherwise set
+         * the offset to 0 and don't configure the update */
+        if (buffer_len == 0) {
+            offset = 0;
+            g_byte_array_append (builder->fixed_buffer, (guint8 *)&offset, sizeof (offset));
+        } else {
+            guint32 offset_offset;
+
+            /* Offset of the offset */
+            offset_offset = builder->fixed_buffer->len;
+
+            /* Length *not* in LE yet */
+            offset = builder->variable_buffer->len;
+            /* Add the offset value */
+            g_byte_array_append (builder->fixed_buffer, (guint8 *)&offset, sizeof (offset));
+            /* Configure the value to get updated */
+            g_array_append_val (builder->offsets, offset_offset);
+        }
+    }
+
+    /* (b) Just length in static buffer, data just afterwards. */
+    if (with_length) {
+        guint32 length;
+
+        /* Add the length value */
+        length = GUINT32_TO_LE (buffer_len);
+        g_byte_array_append (builder->fixed_buffer, (guint8 *)&length, sizeof (length));
+    }
+
+    /* And finally, the bytearray itself to the variable buffer */
+    if (buffer_len) {
+        g_byte_array_append (builder->variable_buffer, (const guint8 *)buffer, (guint)buffer_len);
+
+        while (buffer_len % 4 != 0) {
+            const guint8 padding = 0;
+
+            g_byte_array_append (builder->variable_buffer, &padding, 1);
+            buffer_len++;
+        }
+    }
+}
+
+void
+_mbim_struct_builder_append_uuid (MbimStructBuilder *builder,
+                                  const MbimUuid    *value)
+{
+    static const MbimUuid uuid_invalid = {
+        .a = { 0x00, 0x00, 0x00, 0x00 },
+        .b = { 0x00, 0x00 },
+        .c = { 0x00, 0x00 },
+        .d = { 0x00, 0x00 },
+        .e = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+    };
+
+    /* uuids are added in the static buffer only */
+    g_byte_array_append (builder->fixed_buffer,
+                         value ? (guint8 *)value : (guint8 *)&uuid_invalid,
+                         sizeof (MbimUuid));
+}
+
+void
+_mbim_struct_builder_append_guint32 (MbimStructBuilder *builder,
+                                     guint32            value)
+{
+    guint32 tmp;
+
+    /* guint32 values are added in the static buffer only */
+    tmp = GUINT32_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)
+{
+    guint i;
+
+    /* guint32 array added directly in the static buffer */
+    for (i = 0; i < n_values; i++)
+        _mbim_struct_builder_append_guint32 (builder, values[i]);
+}
+
+void
+_mbim_struct_builder_append_guint64 (MbimStructBuilder *builder,
+                                     guint64            value)
+{
+    guint64 tmp;
+
+    /* guint64 values are added in the static buffer only */
+    tmp = GUINT64_TO_LE (value);
+    g_byte_array_append (builder->fixed_buffer, (guint8 *)&tmp, sizeof (tmp));
+}
+
+void
+_mbim_struct_builder_append_guint64_array (MbimStructBuilder *builder,
+                                           const guint64     *values,
+                                           guint32            n_values)
+{
+    guint i;
+
+    /* guint64 array added directly in the static buffer */
+    for (i = 0; i < n_values; i++)
+        _mbim_struct_builder_append_guint64 (builder, values[i]);
+}
+
+void
+_mbim_struct_builder_append_string (MbimStructBuilder *builder,
+                                    const gchar       *value)
+{
+    guint32 offset;
+    guint32 length;
+    gchar *utf16le = NULL;
+    gsize utf16le_bytes = 0;
+    GError *error = NULL;
+
+    /* A string consists of Offset+Size in the static buffer, plus the
+     * string itself in the variable buffer */
+
+    /* Convert the string from UTF-8 to UTF-16LE */
+    if (value && value[0]) {
+        utf16le = g_convert (value,
+                             -1,
+                             "utf-16le",
+                             "utf-8",
+                             NULL,
+                             &utf16le_bytes,
+                             &error);
+        if (error) {
+            g_warning ("Error converting string: %s", error->message);
+            g_error_free (error);
+            return;
+        }
+    }
+
+    /* If string length is greater than 0, add the offset to fix, otherwise set
+     * the offset to 0 and don't configure the update */
+    if (utf16le_bytes == 0) {
+        offset = 0;
+        g_byte_array_append (builder->fixed_buffer, (guint8 *)&offset, sizeof (offset));
+    } else {
+        guint32 offset_offset;
+
+        /* Offset of the offset */
+        offset_offset = builder->fixed_buffer->len;
+
+        /* Length *not* in LE yet */
+        offset = builder->variable_buffer->len;
+        /* Add the offset value */
+        g_byte_array_append (builder->fixed_buffer, (guint8 *)&offset, sizeof (offset));
+        /* Configure the value to get updated */
+        g_array_append_val (builder->offsets, offset_offset);
+    }
+
+    /* Add the length value */
+    length = GUINT32_TO_LE ((guint32)utf16le_bytes);
+    g_byte_array_append (builder->fixed_buffer, (guint8 *)&length, sizeof (length));
+
+    /* And finally, the string itself to the variable buffer */
+    if (utf16le_bytes) {
+        g_byte_array_append (builder->variable_buffer, (const guint8 *)utf16le, (guint)utf16le_bytes);
+        while (utf16le_bytes % 4 != 0) {
+            const guint8 padding = 0;
+
+            g_byte_array_append (builder->variable_buffer, &padding, 1);
+            utf16le_bytes++;
+        }
+    }
+    g_free (utf16le);
+}
+
+void
+_mbim_struct_builder_append_string_array (MbimStructBuilder  *builder,
+                                          const gchar *const *values,
+                                          guint32             n_values)
+{
+    /* TODO */
+    g_assert_not_reached ();
+}
+
+void
+_mbim_struct_builder_append_ipv4 (MbimStructBuilder *builder,
+                                  const MbimIPv4    *value,
+                                  gboolean           ref)
+{
+    if (ref)
+        _mbim_struct_builder_append_ipv4_array (builder, value, value ? 1 : 0);
+    else
+        g_byte_array_append (builder->fixed_buffer, (guint8 *)value, sizeof (MbimIPv4));
+}
+
+void
+_mbim_struct_builder_append_ipv4_array (MbimStructBuilder *builder,
+                                        const MbimIPv4    *values,
+                                        guint32            n_values)
+{
+    guint32 offset;
+
+    if (!n_values) {
+        offset = 0;
+        g_byte_array_append (builder->fixed_buffer, (guint8 *)&offset, sizeof (offset));
+    } else {
+        guint32 offset_offset;
+
+        /* Offset of the offset */
+        offset_offset = builder->fixed_buffer->len;
+
+        /* Length *not* in LE yet */
+        offset = builder->variable_buffer->len;
+        /* Add the offset value */
+        g_byte_array_append (builder->fixed_buffer, (guint8 *)&offset, sizeof (offset));
+        /* Configure the value to get updated */
+        g_array_append_val (builder->offsets, offset_offset);
+
+        /* NOTE: length of the array must be given in a separate variable */
+
+        /* And finally, the array of IPs itself to the variable buffer */
+        g_byte_array_append (builder->variable_buffer, (guint8 *)values, n_values * sizeof (MbimIPv4));
+    }
+}
+
+void
+_mbim_struct_builder_append_ipv6 (MbimStructBuilder *builder,
+                                  const MbimIPv6    *value,
+                                  gboolean           ref)
+{
+    if (ref)
+        _mbim_struct_builder_append_ipv6_array (builder, value, value ? 1 : 0);
+    else
+        g_byte_array_append (builder->fixed_buffer, (guint8 *)value, sizeof (MbimIPv6));
+}
+
+void
+_mbim_struct_builder_append_ipv6_array (MbimStructBuilder *builder,
+                                        const MbimIPv6    *values,
+                                        guint32            n_values)
+{
+    guint32 offset;
+
+    if (!n_values) {
+        offset = 0;
+        g_byte_array_append (builder->fixed_buffer, (guint8 *)&offset, sizeof (offset));
+    } else {
+        guint32 offset_offset;
+
+        /* Offset of the offset */
+        offset_offset = builder->fixed_buffer->len;
+
+        /* Length *not* in LE yet */
+        offset = builder->variable_buffer->len;
+        /* Add the offset value */
+        g_byte_array_append (builder->fixed_buffer, (guint8 *)&offset, sizeof (offset));
+        /* Configure the value to get updated */
+        g_array_append_val (builder->offsets, offset_offset);
+
+        /* NOTE: length of the array must be given in a separate variable */
+
+        /* And finally, the array of IPs itself to the variable buffer */
+        g_byte_array_append (builder->variable_buffer, (guint8 *)values, n_values * sizeof (MbimIPv6));
+    }
+}
+
+/*****************************************************************************/
+/* Command message builder interface */
+
+MbimMessageCommandBuilder *
+_mbim_message_command_builder_new (guint32                transaction_id,
+                                   MbimService            service,
+                                   guint32                cid,
+                                   MbimMessageCommandType command_type)
+{
+    MbimMessageCommandBuilder *builder;
+
+    builder = g_slice_new (MbimMessageCommandBuilder);
+    builder->message = mbim_message_command_new (transaction_id, service, cid, command_type);
+    builder->contents_builder = _mbim_struct_builder_new ();
+    return builder;
+}
+
+MbimMessage *
+_mbim_message_command_builder_complete (MbimMessageCommandBuilder *builder)
+{
+    MbimMessage *message;
+    GByteArray *contents;
+
+    /* Complete contents, which disposes the builder itself */
+    contents = _mbim_struct_builder_complete (builder->contents_builder);
+
+    /* Merge both buffers */
+    mbim_message_command_append (builder->message,
+                                 (const guint8 *)contents->data,
+                                 (guint32)contents->len);
+    g_byte_array_unref (contents);
+
+    /* Steal the message to return */
+    message = builder->message;
+
+    /* Dispose the remaining stuff from the message builder */
+    g_slice_free (MbimMessageCommandBuilder, builder);
+
+    return message;
+}
+
+void
+_mbim_message_command_builder_append_byte_array (MbimMessageCommandBuilder *builder,
+                                                 gboolean                   with_offset,
+                                                 gboolean                   with_length,
+                                                 const guint8              *buffer,
+                                                 guint32                    buffer_len)
+{
+    _mbim_struct_builder_append_byte_array (builder->contents_builder, with_offset, with_length, buffer, buffer_len);
+}
+
+void
+_mbim_message_command_builder_append_uuid (MbimMessageCommandBuilder *builder,
+                                           const MbimUuid            *value)
+{
+    _mbim_struct_builder_append_uuid (builder->contents_builder, value);
+}
+
+void
+_mbim_message_command_builder_append_guint32 (MbimMessageCommandBuilder *builder,
+                                              guint32                    value)
+{
+    _mbim_struct_builder_append_guint32 (builder->contents_builder, value);
+}
+
+void
+_mbim_message_command_builder_append_guint32_array (MbimMessageCommandBuilder *builder,
+                                                    const guint32             *values,
+                                                    guint32                    n_values)
+{
+    _mbim_struct_builder_append_guint32_array (builder->contents_builder, values, n_values);
+}
+
+void
+_mbim_message_command_builder_append_guint64 (MbimMessageCommandBuilder *builder,
+                                              guint64                    value)
+{
+    _mbim_struct_builder_append_guint64 (builder->contents_builder, value);
+}
+
+void
+_mbim_message_command_builder_append_guint64_array (MbimMessageCommandBuilder *builder,
+                                                    const guint64             *values,
+                                                    guint32                    n_values)
+{
+    _mbim_struct_builder_append_guint64_array (builder->contents_builder, values, n_values);
+}
+
+void
+_mbim_message_command_builder_append_string (MbimMessageCommandBuilder *builder,
+                                             const gchar               *value)
+{
+    _mbim_struct_builder_append_string (builder->contents_builder, value);
+}
+
+void
+_mbim_message_command_builder_append_string_array (MbimMessageCommandBuilder *builder,
+                                                   const gchar *const        *values,
+                                                   guint32                    n_values)
+{
+    _mbim_struct_builder_append_string_array (builder->contents_builder, values, n_values);
+}
+
+void
+_mbim_message_command_builder_append_ipv4 (MbimMessageCommandBuilder *builder,
+                                           const MbimIPv4            *value,
+                                           gboolean                   ref)
+{
+    _mbim_struct_builder_append_ipv4 (builder->contents_builder, value, ref);
+}
+
+void
+_mbim_message_command_builder_append_ipv4_array (MbimMessageCommandBuilder *builder,
+                                                 const MbimIPv4            *values,
+                                                 guint32                    n_values)
+{
+    _mbim_struct_builder_append_ipv4_array (builder->contents_builder, values, n_values);
+}
+
+void
+_mbim_message_command_builder_append_ipv6 (MbimMessageCommandBuilder *builder,
+                                           const MbimIPv6            *value,
+                                           gboolean                   ref)
+{
+    _mbim_struct_builder_append_ipv6 (builder->contents_builder, value, ref);
+}
+
+void
+_mbim_message_command_builder_append_ipv6_array (MbimMessageCommandBuilder *builder,
+                                                 const MbimIPv6            *values,
+                                                 guint32                    n_values)
+{
+    _mbim_struct_builder_append_ipv6_array (builder->contents_builder, values, n_values);
+}
+
+/*****************************************************************************/
+/* Generic message interface */
+
+/**
+ * mbim_message_ref:
+ * @self: a #MbimMessage.
+ *
+ * Atomically increments the reference count of @self by one.
+ *
+ * Returns: (transfer full) the new reference to @self.
+ */
+MbimMessage *
+mbim_message_ref (MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, NULL);
+
+    return (MbimMessage *) g_byte_array_ref ((GByteArray *)self);
+}
+
+/**
+ * mbim_message_unref:
+ * @self: a #MbimMessage.
+ *
+ * Atomically decrements the reference count of @self by one.
+ * If the reference count drops to 0, @self is completely disposed.
+ */
+void
+mbim_message_unref (MbimMessage *self)
+{
+    g_return_if_fail (self != NULL);
+
+    g_byte_array_unref ((GByteArray *)self);
+}
+
+/**
+ * mbim_message_get_message_type:
+ * @self: a #MbimMessage.
+ *
+ * Gets the message type.
+ *
+ * Returns: a #MbimMessageType.
+ */
+MbimMessageType
+mbim_message_get_message_type (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, MBIM_MESSAGE_TYPE_INVALID);
+
+    return MBIM_MESSAGE_GET_MESSAGE_TYPE (self);
+}
+
+/**
+ * mbim_message_get_message_length:
+ * @self: a #MbimMessage.
+ *
+ * Gets the whole message length.
+ *
+ * Returns: the length of the message.
+ */
+guint32
+mbim_message_get_message_length (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, 0);
+
+    return MBIM_MESSAGE_GET_MESSAGE_LENGTH (self);
+}
+
+/**
+ * mbim_message_get_transaction_id:
+ * @self: a #MbimMessage.
+ *
+ * Gets the transaction ID of the message.
+ *
+ * Returns: the transaction ID.
+ */
+guint32
+mbim_message_get_transaction_id (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, 0);
+
+    return MBIM_MESSAGE_GET_TRANSACTION_ID (self);
+}
+
+/**
+ * mbim_message_set_transaction_id:
+ * @self: a #MbimMessage.
+ * @transaction_id: the transaction id.
+ *
+ * Sets the transaction ID of the message.
+ */
+void
+mbim_message_set_transaction_id (MbimMessage *self,
+                                 guint32      transaction_id)
+{
+    /* Only allow setting transaction ID in host-generated messages */
+    g_return_if_fail (self != NULL);
+    g_return_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND ||
+                      MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_OPEN ||
+                      MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_CLOSE ||
+                      MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_HOST_ERROR);
+
+    ((struct header *)(self->data))->transaction_id = GUINT32_TO_LE (transaction_id);
+}
+
+/**
+ * mbim_message_new:
+ * @data: contents of the message.
+ * @data_length: length of the message.
+ *
+ * Create a #MbimMessage with the given contents.
+ *
+ * Returns: (transfer full): a newly created #MbimMessage, which should be freed with mbim_message_unref().
+ */
+MbimMessage *
+mbim_message_new (const guint8 *data,
+                  guint32       data_length)
+{
+    GByteArray *out;
+
+    /* Create output MbimMessage */
+    out = g_byte_array_sized_new (data_length);
+    g_byte_array_append (out, data, data_length);
+
+    return (MbimMessage *)out;
+}
+
+/**
+ * mbim_message_dup:
+ * @self: a #MbimMessage to duplicate.
+ *
+ * Create a #MbimMessage with the same contents as @self.
+ *
+ * Returns: (transfer full): a newly created #MbimMessage, which should be freed with mbim_message_unref().
+ */
+MbimMessage *
+mbim_message_dup (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, NULL);
+
+    return mbim_message_new (((GByteArray *)self)->data,
+                             MBIM_MESSAGE_GET_MESSAGE_LENGTH (self));
+}
+
+/**
+ * mbim_message_get_raw:
+ * @self: a #MbimMessage.
+ * @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 #MbimMessage.
+ *
+ * Returns: (transfer none): The raw data buffer, or #NULL if @error is set.
+ */
+const guint8 *
+mbim_message_get_raw (const MbimMessage  *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,
+                             "Message is empty");
+        return NULL;
+    }
+
+    *length = (guint32) self->len;
+    return self->data;
+}
+
+/**
+ * mbim_message_get_printable:
+ * @self: a #MbimMessage.
+ * @line_prefix: prefix string to use in each new generated line.
+ * @headers_only: %TRUE if only basic headers should be printed.
+ *
+ * Gets a printable string with the contents of the whole MBIM message.
+ *
+ * Returns: (transfer full): a newly allocated string, which should be freed with g_free().
+ */
+gchar *
+mbim_message_get_printable (const MbimMessage *self,
+                            const gchar       *line_prefix,
+                            gboolean           headers_only)
+{
+    GString *printable;
+
+    g_return_val_if_fail (self != NULL, NULL);
+    g_return_val_if_fail (line_prefix != NULL, NULL);
+
+    if (!line_prefix)
+        line_prefix = "";
+
+    printable = g_string_new ("");
+    g_string_append_printf (printable,
+                            "%sHeader:\n"
+                            "%s  length      = %u\n"
+                            "%s  type        = %s (0x%08x)\n"
+                            "%s  transaction = %u\n",
+                            line_prefix,
+                            line_prefix, MBIM_MESSAGE_GET_MESSAGE_LENGTH (self),
+                            line_prefix, mbim_message_type_get_string (MBIM_MESSAGE_GET_MESSAGE_TYPE (self)), MBIM_MESSAGE_GET_MESSAGE_TYPE (self),
+                            line_prefix, MBIM_MESSAGE_GET_TRANSACTION_ID (self));
+
+    switch (MBIM_MESSAGE_GET_MESSAGE_TYPE (self)) {
+    case MBIM_MESSAGE_TYPE_INVALID:
+        g_warn_if_reached ();
+        break;
+
+    case MBIM_MESSAGE_TYPE_OPEN:
+        if (!headers_only)
+            g_string_append_printf (printable,
+                                    "%sContents:\n"
+                                    "%s  max_control_transfer = %u\n",
+                                    line_prefix,
+                                    line_prefix, mbim_message_open_get_max_control_transfer (self));
+        break;
+
+    case MBIM_MESSAGE_TYPE_CLOSE:
+        break;
+
+    case MBIM_MESSAGE_TYPE_OPEN_DONE:
+        if (!headers_only) {
+            MbimStatusError status;
+
+            status = mbim_message_open_done_get_status_code (self);
+            g_string_append_printf (printable,
+                                    "%sContents:\n"
+                                    "%s  status error = '%s' (0x%08x)\n",
+                                    line_prefix,
+                                    line_prefix, mbim_status_error_get_string (status), status);
+        }
+        break;
+
+    case MBIM_MESSAGE_TYPE_CLOSE_DONE:
+        if (!headers_only) {
+            MbimStatusError status;
+
+            status = mbim_message_close_done_get_status_code (self);
+            g_string_append_printf (printable,
+                                    "%sContents:\n"
+                                    "%s  status error = '%s' (0x%08x)\n",
+                                    line_prefix,
+                                    line_prefix, mbim_status_error_get_string (status), status);
+        }
+        break;
+
+    case MBIM_MESSAGE_TYPE_HOST_ERROR:
+    case MBIM_MESSAGE_TYPE_FUNCTION_ERROR:
+        if (!headers_only) {
+            MbimProtocolError error;
+
+            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);
+        }
+        break;
+
+    case MBIM_MESSAGE_TYPE_COMMAND:
+        g_string_append_printf (printable,
+                                "%sFragment header:\n"
+                                "%s  total   = %u\n"
+                                "%s  current = %u\n",
+                                line_prefix,
+                                line_prefix, _mbim_message_fragment_get_total (self),
+                                line_prefix, _mbim_message_fragment_get_current (self));
+        if (!headers_only) {
+            gchar *uuid_printable;
+            const gchar *cid_printable;
+
+            uuid_printable = mbim_uuid_get_printable (mbim_message_command_get_service_id (self));
+            cid_printable = mbim_cid_get_printable (mbim_message_command_get_service (self),
+                                                    mbim_message_command_get_cid (self));
+            g_string_append_printf (printable,
+                                    "%sContents:\n"
+                                    "%s  service = '%s' (%s)\n"
+                                    "%s  cid     = '%s' (0x%08x)\n"
+                                    "%s  type    = '%s' (0x%08x)\n",
+                                    line_prefix,
+                                    line_prefix, mbim_service_get_string (mbim_message_command_get_service (self)), uuid_printable,
+                                    line_prefix, cid_printable, mbim_message_command_get_cid (self),
+                                    line_prefix, mbim_message_command_type_get_string (mbim_message_command_get_command_type (self)), mbim_message_command_get_command_type (self));
+            g_free (uuid_printable);
+        }
+        break;
+
+    case MBIM_MESSAGE_TYPE_COMMAND_DONE:
+        g_string_append_printf (printable,
+                                "%sFragment header:\n"
+                                "%s  total   = %u\n"
+                                "%s  current = %u\n",
+                                line_prefix,
+                                line_prefix, _mbim_message_fragment_get_total (self),
+                                line_prefix, _mbim_message_fragment_get_current (self));
+        if (!headers_only) {
+            gchar *uuid_printable;
+            MbimStatusError status;
+            const gchar *cid_printable;
+
+            status = mbim_message_command_done_get_status_code (self);
+            uuid_printable = mbim_uuid_get_printable (mbim_message_command_done_get_service_id (self));
+            cid_printable = mbim_cid_get_printable (mbim_message_command_done_get_service (self),
+                                                    mbim_message_command_done_get_cid (self));
+            g_string_append_printf (printable,
+                                    "%sContents:\n"
+                                    "%s  status error = '%s' (0x%08x)\n"
+                                    "%s  service      = '%s' (%s)\n"
+                                    "%s  cid          = '%s' (0x%08x)\n",
+                                    line_prefix,
+                                    line_prefix, mbim_status_error_get_string (status), status,
+                                    line_prefix, mbim_service_get_string (mbim_message_command_done_get_service (self)), uuid_printable,
+                                    line_prefix, cid_printable, mbim_message_command_done_get_cid (self));
+            g_free (uuid_printable);
+        }
+        break;
+
+    case MBIM_MESSAGE_TYPE_INDICATE_STATUS:
+        g_string_append_printf (printable,
+                                "%sFragment header:\n"
+                                "%s  total   = %u\n"
+                                "%s  current = %u\n",
+                                line_prefix,
+                                line_prefix, _mbim_message_fragment_get_total (self),
+                                line_prefix, _mbim_message_fragment_get_current (self));
+        if (!headers_only) {
+            gchar *uuid_printable;
+            const gchar *cid_printable;
+
+            uuid_printable = mbim_uuid_get_printable (mbim_message_indicate_status_get_service_id (self));
+            cid_printable = mbim_cid_get_printable (mbim_message_indicate_status_get_service (self),
+                                                    mbim_message_indicate_status_get_cid (self));
+            g_string_append_printf (printable,
+                                    "%sContents:\n"
+                                    "%s  service = '%s' (%s)\n"
+                                    "%s  cid     = '%s' (0x%08x)\n",
+                                    line_prefix,
+                                    line_prefix, mbim_service_get_string (mbim_message_indicate_status_get_service (self)), uuid_printable,
+                                    line_prefix, cid_printable, mbim_message_indicate_status_get_cid (self));
+            g_free (uuid_printable);
+        }
+        break;
+    }
+
+    return g_string_free (printable, FALSE);
+}
+
+/*****************************************************************************/
+/* Fragment interface */
+
+#define MBIM_MESSAGE_IS_FRAGMENT(self)                                  \
+    (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND || \
+     MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND_DONE || \
+     MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_INDICATE_STATUS)
+
+#define MBIM_MESSAGE_FRAGMENT_GET_TOTAL(self)                           \
+	GUINT32_FROM_LE (((struct full_message *)(self->data))->message.fragment.fragment_header.total)
+
+#define MBIM_MESSAGE_FRAGMENT_GET_CURRENT(self)                         \
+	GUINT32_FROM_LE (((struct full_message *)(self->data))->message.fragment.fragment_header.current)
+
+
+gboolean
+_mbim_message_is_fragment (const MbimMessage *self)
+{
+    return MBIM_MESSAGE_IS_FRAGMENT (self);
+}
+
+guint32
+_mbim_message_fragment_get_total (const MbimMessage  *self)
+{
+    g_assert (MBIM_MESSAGE_IS_FRAGMENT (self));
+
+    return MBIM_MESSAGE_FRAGMENT_GET_TOTAL (self);
+}
+
+guint32
+_mbim_message_fragment_get_current (const MbimMessage  *self)
+{
+    g_assert (MBIM_MESSAGE_IS_FRAGMENT (self));
+
+    return MBIM_MESSAGE_FRAGMENT_GET_CURRENT (self);
+}
+
+const guint8 *
+_mbim_message_fragment_get_payload (const MbimMessage *self,
+                                    guint32           *length)
+{
+    g_assert (MBIM_MESSAGE_IS_FRAGMENT (self));
+
+    *length = (MBIM_MESSAGE_GET_MESSAGE_LENGTH (self) - \
+               sizeof (struct header) -                 \
+               sizeof (struct fragment_header));
+    return ((struct full_message *)(self->data))->message.fragment.buffer;
+}
+
+MbimMessage *
+_mbim_message_fragment_collector_init (const MbimMessage  *fragment,
+                                       GError            **error)
+{
+   g_assert (MBIM_MESSAGE_IS_FRAGMENT (fragment));
+
+   /* Collector must start with fragment #0 */
+   if (MBIM_MESSAGE_FRAGMENT_GET_CURRENT (fragment) != 0) {
+       g_set_error (error,
+                    MBIM_PROTOCOL_ERROR,
+                    MBIM_PROTOCOL_ERROR_FRAGMENT_OUT_OF_SEQUENCE,
+                    "Expecting fragment '0/%u', got '%u/%u'",
+                    MBIM_MESSAGE_FRAGMENT_GET_TOTAL (fragment),
+                    MBIM_MESSAGE_FRAGMENT_GET_CURRENT (fragment),
+                    MBIM_MESSAGE_FRAGMENT_GET_TOTAL (fragment));
+       return NULL;
+   }
+
+   return mbim_message_dup (fragment);
+}
+
+gboolean
+_mbim_message_fragment_collector_add (MbimMessage        *self,
+                                      const MbimMessage  *fragment,
+                                      GError            **error)
+{
+    guint32 buffer_len;
+    const guint8 *buffer;
+
+    g_assert (MBIM_MESSAGE_IS_FRAGMENT (self));
+    g_assert (MBIM_MESSAGE_IS_FRAGMENT (fragment));
+
+    /* We can only add a fragment if it is the next one we're expecting.
+     * Otherwise, we return an error. */
+    if (MBIM_MESSAGE_FRAGMENT_GET_CURRENT (self) != (MBIM_MESSAGE_FRAGMENT_GET_CURRENT (fragment) - 1)) {
+        g_set_error (error,
+                     MBIM_PROTOCOL_ERROR,
+                     MBIM_PROTOCOL_ERROR_FRAGMENT_OUT_OF_SEQUENCE,
+                     "Expecting fragment '%u/%u', got '%u/%u'",
+                     MBIM_MESSAGE_FRAGMENT_GET_CURRENT (self) + 1,
+                     MBIM_MESSAGE_FRAGMENT_GET_TOTAL (self),
+                     MBIM_MESSAGE_FRAGMENT_GET_CURRENT (fragment),
+                     MBIM_MESSAGE_FRAGMENT_GET_TOTAL (fragment));
+        return FALSE;
+    }
+
+    buffer = _mbim_message_fragment_get_payload (fragment, &buffer_len);
+    if (buffer_len) {
+        /* Concatenate information buffers */
+        g_byte_array_append ((GByteArray *)self, buffer, buffer_len);
+        /* Update the whole message length */
+        ((struct header *)(self->data))->length =
+            GUINT32_TO_LE (MBIM_MESSAGE_GET_MESSAGE_LENGTH (self) + buffer_len);
+    }
+
+    /* Update the current fragment info in the main message; skip endian changes */
+    ((struct full_message *)(self->data))->message.fragment.fragment_header.current =
+        ((struct full_message *)(fragment->data))->message.fragment.fragment_header.current;
+
+    return TRUE;
+}
+
+gboolean
+_mbim_message_fragment_collector_complete (MbimMessage *self)
+{
+    g_assert (MBIM_MESSAGE_IS_FRAGMENT (self));
+
+    if (MBIM_MESSAGE_FRAGMENT_GET_CURRENT (self) != (MBIM_MESSAGE_FRAGMENT_GET_TOTAL (self) - 1))
+        /* Not complete yet */
+        return FALSE;
+
+    /* Reset current & total */
+    ((struct full_message *)(self->data))->message.fragment.fragment_header.current = 0;
+    ((struct full_message *)(self->data))->message.fragment.fragment_header.total = GUINT32_TO_LE (1);
+    return TRUE;
+}
+
+struct fragment_info *
+_mbim_message_split_fragments (const MbimMessage *self,
+                               guint32            max_fragment_size,
+                               guint             *n_fragments)
+{
+    GArray *array;
+    guint32 total_message_length;
+    guint32 total_payload_length;
+    guint32 fragment_header_length;
+    guint32 fragment_payload_length;
+    guint32 total_fragments;
+    guint i;
+    const guint8 *data;
+    guint32 data_length;
+
+    /* A message which is longer than the maximum fragment size needs to be
+     * split in different fragments before sending it. */
+
+    total_message_length = mbim_message_get_message_length (self);
+
+    /* If a single fragment is enough, don't try to split */
+    if (total_message_length <= max_fragment_size)
+        return NULL;
+
+    /* Total payload length is the total length minus the headers of the
+     * input message */
+    fragment_header_length = sizeof (struct header) + sizeof (struct fragment_header);
+    total_payload_length = total_message_length - fragment_header_length;
+
+    /* Fragment payload length is the maximum amount of data that can fit in a
+     * single fragment */
+    fragment_payload_length = max_fragment_size - fragment_header_length;
+
+    /* We can now compute the number of fragments that we'll get */
+    total_fragments = total_payload_length / fragment_payload_length;
+    if (total_payload_length % fragment_payload_length)
+        total_fragments++;
+
+    array = g_array_sized_new (FALSE,
+                               FALSE,
+                               sizeof (struct fragment_info),
+                               total_fragments);
+
+    /* Initialize data walkers */
+    data = ((struct full_message *)(((GByteArray *)self)->data))->message.fragment.buffer;
+    data_length = total_payload_length;
+
+    /* Create fragment infos */
+    for (i = 0; i < total_fragments; i++) {
+        struct fragment_info info;
+
+        /* Set data info */
+        info.data = data;
+        info.data_length = (data_length > fragment_payload_length ? fragment_payload_length : data_length);
+
+        /* Set header info */
+        info.header.type             = GUINT32_TO_LE (MBIM_MESSAGE_GET_MESSAGE_TYPE (self));
+        info.header.length           = GUINT32_TO_LE (fragment_header_length + info.data_length);
+        info.header.transaction_id   = GUINT32_TO_LE (MBIM_MESSAGE_GET_TRANSACTION_ID (self));
+        info.fragment_header.total   = GUINT32_TO_LE (total_fragments);
+        info.fragment_header.current = GUINT32_TO_LE (i);
+
+        g_array_insert_val (array, i, info);
+
+        /* Update walkers */
+        data = &data[info.data_length];
+        data_length -= info.data_length;
+    }
+
+    g_warn_if_fail (data_length == 0);
+
+    *n_fragments = total_fragments;
+    return (struct fragment_info *) g_array_free (array, FALSE);
+}
+
+/*****************************************************************************/
+/* 'Open' message interface */
+
+/**
+ * mbim_message_open_new:
+ * @transaction_id: transaction ID.
+ * @max_control_transfer: maximum control transfer.
+ *
+ * Create a new #MbimMessage of type %MBIM_MESSAGE_TYPE_OPEN with the specified
+ * parameters.
+ *
+ * Returns: (transfer full): a newly created #MbimMessage. The returned value
+ * should be freed with mbim_message_unref().
+ */
+MbimMessage *
+mbim_message_open_new (guint32 transaction_id,
+                       guint32 max_control_transfer)
+{
+    GByteArray *self;
+
+    self = _mbim_message_allocate (MBIM_MESSAGE_TYPE_OPEN,
+                                   transaction_id,
+                                   sizeof (struct open_message));
+
+    /* Open header */
+    ((struct full_message *)(self->data))->message.open.max_control_transfer = GUINT32_TO_LE (max_control_transfer);
+
+    return (MbimMessage *)self;
+}
+
+/**
+ * mbim_message_open_get_max_control_transfer:
+ * @self: a #MbimMessage.
+ *
+ * Get the maximum control transfer set to be used in the #MbimMessage of type
+ * %MBIM_MESSAGE_TYPE_OPEN.
+ *
+ * Returns: the maximum control transfer.
+ */
+guint32
+mbim_message_open_get_max_control_transfer (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, 0);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_OPEN, 0);
+
+    return GUINT32_FROM_LE (((struct full_message *)(self->data))->message.open.max_control_transfer);
+}
+
+/*****************************************************************************/
+/* 'Open Done' message interface */
+
+/**
+ * mbim_message_open_done_get_status_code:
+ * @self: a #MbimMessage.
+ *
+ * Get status code from the %MBIM_MESSAGE_TYPE_OPEN_DONE message.
+ *
+ * Returns: a #MbimStatusError.
+ */
+MbimStatusError
+mbim_message_open_done_get_status_code (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, MBIM_STATUS_ERROR_FAILURE);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_OPEN_DONE, MBIM_STATUS_ERROR_FAILURE);
+
+    return (MbimStatusError) GUINT32_FROM_LE (((struct full_message *)(self->data))->message.open_done.status_code);
+}
+
+/**
+ * mbim_message_open_done_get_result:
+ * @self: a #MbimMessage.
+ * @error: return location for error or %NULL.
+ *
+ * Gets the result of the 'Open' operation in the %MBIM_MESSAGE_TYPE_OPEN_DONE message.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ */
+gboolean
+mbim_message_open_done_get_result (const MbimMessage  *self,
+                                   GError            **error)
+{
+    MbimStatusError status;
+
+    g_return_val_if_fail (self != NULL, FALSE);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_OPEN_DONE, FALSE);
+
+    status = (MbimStatusError) GUINT32_FROM_LE (((struct full_message *)(self->data))->message.open_done.status_code);
+    if (status == MBIM_STATUS_ERROR_NONE)
+        return TRUE;
+
+    g_set_error_literal (error,
+                         MBIM_STATUS_ERROR,
+                         status,
+                         mbim_status_error_get_string (status));
+    return FALSE;
+}
+
+/*****************************************************************************/
+/* 'Close' message interface */
+
+/**
+ * mbim_message_close_new:
+ * @transaction_id: transaction ID.
+ *
+ * Create a new #MbimMessage of type %MBIM_MESSAGE_TYPE_CLOSE with the specified
+ * parameters.
+ *
+ * Returns: (transfer full): a newly created #MbimMessage. The returned value
+ * should be freed with mbim_message_unref().
+ */
+MbimMessage *
+mbim_message_close_new (guint32 transaction_id)
+{
+    return (MbimMessage *) _mbim_message_allocate (MBIM_MESSAGE_TYPE_CLOSE,
+                                                   transaction_id,
+                                                   0);
+}
+
+/*****************************************************************************/
+/* 'Close Done' message interface */
+
+/**
+ * mbim_message_close_done_get_status_code:
+ * @self: a #MbimMessage.
+ *
+ * Get status code from the %MBIM_MESSAGE_TYPE_CLOSE_DONE message.
+ *
+ * Returns: a #MbimStatusError.
+ */
+MbimStatusError
+mbim_message_close_done_get_status_code (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, MBIM_STATUS_ERROR_FAILURE);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_CLOSE_DONE, MBIM_STATUS_ERROR_FAILURE);
+
+    return (MbimStatusError) GUINT32_FROM_LE (((struct full_message *)(self->data))->message.close_done.status_code);
+}
+
+/**
+ * mbim_message_close_done_get_result:
+ * @self: a #MbimMessage.
+ * @error: return location for error or %NULL.
+ *
+ * Gets the result of the 'Close' operation in the %MBIM_MESSAGE_TYPE_CLOSE_DONE message.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ */
+gboolean
+mbim_message_close_done_get_result (const MbimMessage  *self,
+                                    GError            **error)
+{
+    MbimStatusError status;
+
+    g_return_val_if_fail (self != NULL, FALSE);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_CLOSE_DONE, FALSE);
+
+    status = (MbimStatusError) GUINT32_FROM_LE (((struct full_message *)(self->data))->message.close_done.status_code);
+    if (status == MBIM_STATUS_ERROR_NONE)
+        return TRUE;
+
+    g_set_error_literal (error,
+                         MBIM_STATUS_ERROR,
+                         status,
+                         mbim_status_error_get_string (status));
+    return FALSE;
+}
+
+/*****************************************************************************/
+/* 'Error' message interface */
+
+/**
+ * mbim_message_error_new:
+ * @transaction_id: transaction ID.
+ * @error_status_code: a #MbimProtocolError.
+ *
+ * Create a new #MbimMessage of type %MBIM_MESSAGE_TYPE_HOST_ERROR with the specified
+ * parameters.
+ *
+ * Returns: (transfer full): a newly created #MbimMessage. The returned value
+ * should be freed with mbim_message_unref().
+ */
+MbimMessage *
+mbim_message_error_new (guint32           transaction_id,
+                        MbimProtocolError error_status_code)
+{
+    GByteArray *self;
+
+    self = _mbim_message_allocate (MBIM_MESSAGE_TYPE_HOST_ERROR,
+                                   transaction_id,
+                                   sizeof (struct open_message));
+
+    /* Open header */
+    ((struct full_message *)(self->data))->message.error.error_status_code = GUINT32_TO_LE (error_status_code);
+
+    return (MbimMessage *)self;
+}
+
+/**
+ * mbim_message_error_get_error_status_code:
+ * @self: a #MbimMessage.
+ *
+ * Get the error code in a %MBIM_MESSAGE_TYPE_HOST_ERROR or
+ * %MBIM_MESSAGE_TYPE_FUNCTION_ERROR message.
+ *
+ * Returns: a #MbimProtocolError.
+ */
+MbimProtocolError
+mbim_message_error_get_error_status_code (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, MBIM_PROTOCOL_ERROR_INVALID);
+    g_return_val_if_fail ((MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_HOST_ERROR ||
+                           MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_FUNCTION_ERROR),
+                          MBIM_PROTOCOL_ERROR_INVALID);
+
+    return (MbimProtocolError) GUINT32_FROM_LE (((struct full_message *)(self->data))->message.error.error_status_code);
+}
+
+/**
+ * mbim_message_error_get_error:
+ * @self: a #MbimMessage.
+ *
+ * Get the error in a %MBIM_MESSAGE_TYPE_HOST_ERROR or
+ * %MBIM_MESSAGE_TYPE_FUNCTION_ERROR message.
+ *
+ * Returns: a newly allocated #GError, which should be freed with g_error_free().
+ */
+GError *
+mbim_message_error_get_error (const MbimMessage *self)
+{
+    MbimProtocolError error_status_code;
+
+
+    g_return_val_if_fail (self != NULL, NULL);
+    g_return_val_if_fail ((MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_HOST_ERROR ||
+                           MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_FUNCTION_ERROR),
+                          NULL);
+
+    error_status_code = (MbimProtocolError) GUINT32_FROM_LE (((struct full_message *)(self->data))->message.error.error_status_code);
+
+    return g_error_new (MBIM_PROTOCOL_ERROR,
+                        error_status_code,
+                        "MBIM protocol error: %s",
+                        mbim_protocol_error_get_string (error_status_code));
+}
+
+/*****************************************************************************/
+/* 'Command' message interface */
+
+/**
+ * mbim_message_command_new:
+ * @transaction_id: transaction ID.
+ * @service: a #MbimService.
+ * @cid: the command ID.
+ * @command_type: the command type.
+ *
+ * Create a new #MbimMessage of type %MBIM_MESSAGE_TYPE_COMMAND with the
+ * specified parameters and an empty information buffer.
+ *
+ * Returns: (transfer full): a newly created #MbimMessage. The returned value
+ * should be freed with mbim_message_unref().
+ */
+MbimMessage *
+mbim_message_command_new (guint32                transaction_id,
+                          MbimService            service,
+                          guint32                cid,
+                          MbimMessageCommandType command_type)
+{
+    GByteArray *self;
+    const MbimUuid *service_id;
+
+    /* Known service required */
+    g_return_val_if_fail (service > MBIM_SERVICE_INVALID, FALSE);
+    g_return_val_if_fail (service <= MBIM_SERVICE_DSS, FALSE);
+    service_id = mbim_uuid_from_service (service);
+
+    self = _mbim_message_allocate (MBIM_MESSAGE_TYPE_COMMAND,
+                                   transaction_id,
+                                   sizeof (struct command_message));
+
+    /* Fragment header */
+    ((struct full_message *)(self->data))->message.command.fragment_header.total   = GUINT32_TO_LE (1);
+    ((struct full_message *)(self->data))->message.command.fragment_header.current = 0;
+
+    /* Command header */
+    memcpy (((struct full_message *)(self->data))->message.command.service_id, service_id, sizeof (*service_id));
+    ((struct full_message *)(self->data))->message.command.command_id    = GUINT32_TO_LE (cid);
+    ((struct full_message *)(self->data))->message.command.command_type  = GUINT32_TO_LE (command_type);
+    ((struct full_message *)(self->data))->message.command.buffer_length = 0;
+
+    return (MbimMessage *)self;
+}
+
+/**
+ * mbim_message_command_append:
+ * @self: a #MbimMessage.
+ * @buffer: raw buffer to append to the message.
+ * @buffer_size: length of the data in @buffer.
+ *
+ * Appends the contents of @buffer to @self.
+ */
+void
+mbim_message_command_append (MbimMessage  *self,
+                             const guint8 *buffer,
+                             guint32       buffer_size)
+{
+    g_byte_array_append ((GByteArray *)self, buffer, buffer_size);
+
+    /* Update message and buffer length */
+    ((struct header *)(self->data))->length =
+        GUINT32_TO_LE (MBIM_MESSAGE_GET_MESSAGE_LENGTH (self) + buffer_size);
+    ((struct full_message *)(self->data))->message.command.buffer_length =
+        GUINT32_TO_LE (GUINT32_FROM_LE (((struct full_message *)(self->data))->message.command.buffer_length) + buffer_size);
+}
+
+/**
+ * mbim_message_command_get_service:
+ * @self: a #MbimMessage.
+ *
+ * Get the service of a %MBIM_MESSAGE_TYPE_COMMAND message.
+ *
+ * Returns: a #MbimService.
+ */
+MbimService
+mbim_message_command_get_service (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, MBIM_SERVICE_INVALID);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND, MBIM_SERVICE_INVALID);
+
+    return mbim_uuid_to_service ((const MbimUuid *)&(((struct full_message *)(self->data))->message.command.service_id));
+}
+
+/**
+ * mbim_message_command_get_service_id:
+ * @self: a #MbimMessage.
+ *
+ * Get the service UUID of a %MBIM_MESSAGE_TYPE_COMMAND message.
+ *
+ * Returns: a #MbimUuid.
+ */
+const MbimUuid *
+mbim_message_command_get_service_id (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, MBIM_UUID_INVALID);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND, MBIM_UUID_INVALID);
+
+    return (const MbimUuid *)&(((struct full_message *)(self->data))->message.command.service_id);
+}
+
+/**
+ * mbim_message_command_get_cid:
+ * @self: a #MbimMessage.
+ *
+ * Get the command id of a %MBIM_MESSAGE_TYPE_COMMAND message.
+ *
+ * Returns: a CID.
+ */
+guint32
+mbim_message_command_get_cid (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, 0);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND, 0);
+
+    return GUINT32_FROM_LE (((struct full_message *)(self->data))->message.command.command_id);
+}
+
+/**
+ * mbim_message_command_get_command_type:
+ * @self: a #MbimMessage.
+ *
+ * Get the command type of a %MBIM_MESSAGE_TYPE_COMMAND message.
+ *
+ * Returns: a #MbimMessageCommandType.
+ */
+MbimMessageCommandType
+mbim_message_command_get_command_type (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, MBIM_MESSAGE_COMMAND_TYPE_UNKNOWN);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND, MBIM_MESSAGE_COMMAND_TYPE_UNKNOWN);
+
+    return (MbimMessageCommandType) GUINT32_FROM_LE (((struct full_message *)(self->data))->message.command.command_type);
+}
+
+/**
+ * mbim_message_command_get_raw_information_buffer:
+ * @self: a #MbimMessage.
+ * @length: (out): return location for the size of the output buffer.
+ *
+ * Gets the information buffer of the %MBIM_MESSAGE_TYPE_COMMAND message.
+ *
+ * Returns: (transfer none): The raw data buffer, or #NULL if empty.
+ */
+const guint8 *
+mbim_message_command_get_raw_information_buffer (const MbimMessage *self,
+                                                 guint32           *length)
+{
+    g_return_val_if_fail (self != NULL, NULL);
+    g_return_val_if_fail (length != NULL, NULL);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND, NULL);
+
+    *length = GUINT32_FROM_LE (((struct full_message *)(self->data))->message.command.buffer_length);
+
+    return (*length > 0 ?
+            ((struct full_message *)(self->data))->message.command.buffer :
+            NULL);
+}
+
+/*****************************************************************************/
+/* 'Command Done' message interface */
+
+/**
+ * mbim_message_command_done_get_service:
+ * @self: a #MbimMessage.
+ *
+ * Get the service of a %MBIM_MESSAGE_TYPE_COMMAND_DONE message.
+ *
+ * Returns: a #MbimService.
+ */
+MbimService
+mbim_message_command_done_get_service (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, MBIM_SERVICE_INVALID);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND_DONE, MBIM_SERVICE_INVALID);
+
+    return mbim_uuid_to_service ((const MbimUuid *)&(((struct full_message *)(self->data))->message.command_done.service_id));
+}
+
+/**
+ * mbim_message_command_done_get_service_id:
+ * @self: a #MbimMessage.
+ *
+ * Get the service UUID of a %MBIM_MESSAGE_TYPE_COMMAND_DONE message.
+ *
+ * Returns: a #MbimUuid.
+ */
+const MbimUuid *
+mbim_message_command_done_get_service_id (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, MBIM_UUID_INVALID);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND_DONE, MBIM_UUID_INVALID);
+
+    return (const MbimUuid *)&(((struct full_message *)(self->data))->message.command_done.service_id);
+}
+
+/**
+ * mbim_message_command_done_get_cid:
+ * @self: a #MbimMessage.
+ *
+ * Get the command id of a %MBIM_MESSAGE_TYPE_COMMAND_DONE message.
+ *
+ * Returns: a CID.
+ */
+guint32
+mbim_message_command_done_get_cid (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, 0);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND_DONE, 0);
+
+    return GUINT32_FROM_LE (((struct full_message *)(self->data))->message.command_done.command_id);
+}
+
+/**
+ * mbim_message_command_done_get_status_code:
+ * @self: a #MbimMessage.
+ *
+ * Get status code from the %MBIM_MESSAGE_TYPE_COMMAND_DONE message.
+ *
+ * Returns: a #MbimStatusError.
+ */
+MbimStatusError
+mbim_message_command_done_get_status_code (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, MBIM_STATUS_ERROR_FAILURE);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND_DONE, MBIM_STATUS_ERROR_FAILURE);
+
+    return (MbimStatusError) GUINT32_FROM_LE (((struct full_message *)(self->data))->message.command_done.status_code);
+}
+
+/**
+ * mbim_message_command_done_get_result:
+ * @self: a #MbimMessage.
+ * @error: return location for error or %NULL.
+ *
+ * Gets the result of the 'Command' operation in the %MBIM_MESSAGE_TYPE_COMMAND_DONE message.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ */
+gboolean
+mbim_message_command_done_get_result (const MbimMessage  *self,
+                                      GError            **error)
+{
+    MbimStatusError status;
+
+    g_return_val_if_fail (self != NULL, FALSE);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND_DONE, FALSE);
+
+    status = (MbimStatusError) GUINT32_FROM_LE (((struct full_message *)(self->data))->message.command_done.status_code);
+    if (status == MBIM_STATUS_ERROR_NONE)
+        return TRUE;
+
+    g_set_error_literal (error,
+                         MBIM_STATUS_ERROR,
+                         status,
+                         mbim_status_error_get_string (status));
+    return FALSE;
+}
+
+/**
+ * mbim_message_command_done_get_raw_information_buffer:
+ * @self: a #MbimMessage.
+ * @length: (out): return location for the size of the output buffer.
+ *
+ * Gets the information buffer of the %MBIM_MESSAGE_TYPE_COMMAND_DONE message.
+ *
+ * Returns: (transfer none): The raw data buffer, or #NULL if empty.
+ */
+const guint8 *
+mbim_message_command_done_get_raw_information_buffer (const MbimMessage *self,
+                                                      guint32           *length)
+{
+    g_return_val_if_fail (self != NULL, NULL);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_COMMAND_DONE, NULL);
+    g_return_val_if_fail (length != NULL, NULL);
+
+    *length = GUINT32_FROM_LE (((struct full_message *)(self->data))->message.command_done.buffer_length);
+
+    return (*length > 0 ?
+            ((struct full_message *)(self->data))->message.command_done.buffer :
+            NULL);
+}
+
+/*****************************************************************************/
+/* 'Indicate Status' message interface */
+
+/**
+ * mbim_message_indicate_status_get_service:
+ * @self: a #MbimMessage.
+ *
+ * Get the service of a %MBIM_MESSAGE_TYPE_INDICATE_STATUS message.
+ *
+ * Returns: a #MbimService.
+ */
+MbimService
+mbim_message_indicate_status_get_service (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, MBIM_SERVICE_INVALID);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_INDICATE_STATUS, MBIM_SERVICE_INVALID);
+
+    return mbim_uuid_to_service ((const MbimUuid *)&(((struct full_message *)(self->data))->message.indicate_status.service_id));
+}
+
+/**
+ * mbim_message_indicate_status_get_service_id:
+ * @self: a #MbimMessage.
+ *
+ * Get the service UUID of a %MBIM_MESSAGE_TYPE_INDICATE_STATUS message.
+ *
+ * Returns: a #MbimUuid.
+ */
+const MbimUuid *
+mbim_message_indicate_status_get_service_id (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, MBIM_UUID_INVALID);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_INDICATE_STATUS, MBIM_UUID_INVALID);
+
+    return (const MbimUuid *)&(((struct full_message *)(self->data))->message.indicate_status.service_id);
+}
+
+/**
+ * mbim_message_indicate_status_get_cid:
+ * @self: a #MbimMessage.
+ *
+ * Get the command id of a %MBIM_MESSAGE_TYPE_INDICATE_STATUS message.
+ *
+ * Returns: a CID.
+ */
+guint32
+mbim_message_indicate_status_get_cid (const MbimMessage *self)
+{
+    g_return_val_if_fail (self != NULL, 0);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_INDICATE_STATUS, 0);
+
+    return GUINT32_FROM_LE (((struct full_message *)(self->data))->message.indicate_status.command_id);
+}
+
+/**
+ * mbim_message_indicate_status_get_raw_information_buffer:
+ * @self: a #MbimMessage.
+ * @length: (out): return location for the size of the output buffer.
+ *
+ * Gets the information buffer of the %MBIM_MESSAGE_TYPE_INDICATE_STATUS message.
+ *
+ * Returns: (transfer none): The raw data buffer, or #NULL if empty.
+ */
+const guint8 *
+mbim_message_indicate_status_get_raw_information_buffer (const MbimMessage *self,
+                                                         guint32           *length)
+{
+    g_return_val_if_fail (self != NULL, NULL);
+    g_return_val_if_fail (MBIM_MESSAGE_GET_MESSAGE_TYPE (self) == MBIM_MESSAGE_TYPE_INDICATE_STATUS, NULL);
+    g_return_val_if_fail (length != NULL, NULL);
+
+    *length = GUINT32_FROM_LE (((struct full_message *)(self->data))->message.indicate_status.buffer_length);
+
+    return (*length > 0 ?
+            ((struct full_message *)(self->data))->message.indicate_status.buffer :
+            NULL);
+}
diff --git a/libmbim-glib/mbim-message.h b/libmbim-glib/mbim-message.h
new file mode 100644
index 0000000..d5ac8e5
--- /dev/null
+++ b/libmbim-glib/mbim-message.h
@@ -0,0 +1,213 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#ifndef _LIBMBIM_GLIB_MBIM_MESSAGE_H_
+#define _LIBMBIM_GLIB_MBIM_MESSAGE_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"
+#include "mbim-errors.h"
+
+G_BEGIN_DECLS
+
+/**
+ * MbimMessage:
+ *
+ * An opaque type representing a MBIM message.
+ */
+typedef struct _MbimMessage MbimMessage;
+
+GType mbim_message_get_type (void) G_GNUC_CONST;
+#define MBIM_TYPE_MESSAGE (mbim_message_get_type ())
+
+/**
+ * MbimIPv4:
+ * @addr: 4 bytes specifying the IPv4 address.
+ *
+ * An IPv4 address.
+ */
+typedef struct _MbimIPv4 MbimIPv4;
+struct _MbimIPv4 {
+    guint8 addr[4];
+};
+
+/**
+ * MbimIPv6:
+ * @addr: 16 bytes specifying the IPv6 address.
+ *
+ * An IPv6 address.
+ */
+typedef struct _MbimIPv6 MbimIPv6;
+struct _MbimIPv6 {
+    guint8 addr[16];
+};
+
+/**
+ * MbimMessageType:
+ * @MBIM_MESSAGE_TYPE_INVALID: Invalid MBIM message.
+ * @MBIM_MESSAGE_TYPE_OPEN: Initialization request.
+ * @MBIM_MESSAGE_TYPE_CLOSE: Close request.
+ * @MBIM_MESSAGE_TYPE_COMMAND: Command request.
+ * @MBIM_MESSAGE_TYPE_HOST_ERROR: Host-reported error in the communication.
+ * @MBIM_MESSAGE_TYPE_OPEN_DONE: Response to initialization request.
+ * @MBIM_MESSAGE_TYPE_CLOSE_DONE: Response to close request.
+ * @MBIM_MESSAGE_TYPE_COMMAND_DONE: Response to command request.
+ * @MBIM_MESSAGE_TYPE_FUNCTION_ERROR: Function-reported error in the communication.
+ * @MBIM_MESSAGE_TYPE_INDICATE_STATUS: Unsolicited message from the function.
+ *
+ * Type of MBIM messages.
+ */
+typedef enum {
+    MBIM_MESSAGE_TYPE_INVALID         = 0x00000000,
+    /* From Host to Function */
+    MBIM_MESSAGE_TYPE_OPEN            = 0x00000001,
+    MBIM_MESSAGE_TYPE_CLOSE           = 0x00000002,
+    MBIM_MESSAGE_TYPE_COMMAND         = 0x00000003,
+    MBIM_MESSAGE_TYPE_HOST_ERROR      = 0x00000004,
+    /* From Function to Host */
+    MBIM_MESSAGE_TYPE_OPEN_DONE       = 0x80000001,
+    MBIM_MESSAGE_TYPE_CLOSE_DONE      = 0x80000002,
+    MBIM_MESSAGE_TYPE_COMMAND_DONE    = 0x80000003,
+    MBIM_MESSAGE_TYPE_FUNCTION_ERROR  = 0x80000004,
+    MBIM_MESSAGE_TYPE_INDICATE_STATUS = 0x80000007
+} MbimMessageType;
+
+/*****************************************************************************/
+/* Generic message interface */
+
+MbimMessage     *mbim_message_new                  (const guint8       *data,
+                                                    guint32             data_length);
+MbimMessage     *mbim_message_dup                  (const MbimMessage  *self);
+MbimMessage     *mbim_message_ref                  (MbimMessage        *self);
+void             mbim_message_unref                (MbimMessage        *self);
+
+gchar           *mbim_message_get_printable        (const MbimMessage  *self,
+                                                    const gchar        *line_prefix,
+                                                    gboolean            headers_only);
+const guint8    *mbim_message_get_raw              (const MbimMessage  *self,
+                                                    guint32            *length,
+                                                    GError            **error);
+
+MbimMessageType  mbim_message_get_message_type     (const MbimMessage  *self);
+guint32          mbim_message_get_message_length   (const MbimMessage  *self);
+guint32          mbim_message_get_transaction_id   (const MbimMessage  *self);
+
+void             mbim_message_set_transaction_id   (MbimMessage        *self,
+                                                    guint32             transaction_id);
+
+/*****************************************************************************/
+/* 'Open' message interface */
+
+MbimMessage *mbim_message_open_new                      (guint32            transaction_id,
+                                                         guint32            max_control_transfer);
+guint32      mbim_message_open_get_max_control_transfer (const MbimMessage *self);
+
+/*****************************************************************************/
+/* 'Open Done' message interface */
+
+MbimStatusError mbim_message_open_done_get_status_code (const MbimMessage  *self);
+gboolean        mbim_message_open_done_get_result      (const MbimMessage  *self,
+                                                        GError            **error);
+
+/*****************************************************************************/
+/* 'Close' message interface */
+
+MbimMessage *mbim_message_close_new (guint32 transaction_id);
+
+/*****************************************************************************/
+/* 'Close Done' message interface */
+
+MbimStatusError mbim_message_close_done_get_status_code (const MbimMessage  *self);
+gboolean        mbim_message_close_done_get_result      (const MbimMessage  *self,
+                                                         GError            **error);
+
+/*****************************************************************************/
+/* 'Error' message interface */
+
+MbimMessage       *mbim_message_error_new                   (guint32            transaction_id,
+                                                             MbimProtocolError  error_status_code);
+MbimProtocolError  mbim_message_error_get_error_status_code (const MbimMessage *self);
+GError            *mbim_message_error_get_error             (const MbimMessage *self);
+
+/*****************************************************************************/
+/* 'Command' message interface */
+
+/**
+ * MbimMessageCommandType:
+ * @MBIM_MESSAGE_COMMAND_TYPE_UNKNOWN: Unknown type.
+ * @MBIM_MESSAGE_COMMAND_TYPE_QUERY: Query command.
+ * @MBIM_MESSAGE_COMMAND_TYPE_SET: Set command.
+ *
+ * Type of command message.
+ */
+typedef enum {
+    MBIM_MESSAGE_COMMAND_TYPE_UNKNOWN = -1,
+    MBIM_MESSAGE_COMMAND_TYPE_QUERY   = 0,
+    MBIM_MESSAGE_COMMAND_TYPE_SET     = 1
+} MbimMessageCommandType;
+
+MbimMessage *mbim_message_command_new    (guint32                transaction_id,
+                                          MbimService            service,
+                                          guint32                cid,
+                                          MbimMessageCommandType command_type);
+void         mbim_message_command_append (MbimMessage            *self,
+                                          const guint8           *buffer,
+                                          guint32                 buffer_size);
+
+MbimService             mbim_message_command_get_service                (const MbimMessage *self);
+const MbimUuid         *mbim_message_command_get_service_id             (const MbimMessage *self);
+guint32                 mbim_message_command_get_cid                    (const MbimMessage *self);
+MbimMessageCommandType  mbim_message_command_get_command_type           (const MbimMessage *self);
+const guint8           *mbim_message_command_get_raw_information_buffer (const MbimMessage *self,
+                                                                         guint32           *length);
+
+/*****************************************************************************/
+/* 'Command Done' message interface */
+
+MbimService      mbim_message_command_done_get_service                (const MbimMessage  *self);
+const MbimUuid  *mbim_message_command_done_get_service_id             (const MbimMessage  *self);
+guint32          mbim_message_command_done_get_cid                    (const MbimMessage  *self);
+MbimStatusError  mbim_message_command_done_get_status_code            (const MbimMessage  *self);
+gboolean         mbim_message_command_done_get_result                 (const MbimMessage  *self,
+                                                                       GError            **error);
+const guint8    *mbim_message_command_done_get_raw_information_buffer (const MbimMessage  *self,
+                                                                       guint32            *length);
+
+/*****************************************************************************/
+/* 'Indicate Status' message interface */
+
+MbimService      mbim_message_indicate_status_get_service                (const MbimMessage  *self);
+const MbimUuid  *mbim_message_indicate_status_get_service_id             (const MbimMessage  *self);
+guint32          mbim_message_indicate_status_get_cid                    (const MbimMessage  *self);
+const guint8    *mbim_message_indicate_status_get_raw_information_buffer (const MbimMessage  *self,
+                                                                          guint32            *length);
+
+G_END_DECLS
+
+#endif /* _LIBMBIM_GLIB_MBIM_MESSAGE_H_ */
diff --git a/libmbim-glib/mbim-utils.c b/libmbim-glib/mbim-utils.c
new file mode 100644
index 0000000..e2220c0
--- /dev/null
+++ b/libmbim-glib/mbim-utils.c
@@ -0,0 +1,102 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@lanedo.com>
+ */
+
+#include <config.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "mbim-utils.h"
+
+/**
+ * SECTION:mbim-utils
+ * @title: Common utilities
+ *
+ * This section exposes a set of common utilities that may be used to work
+ * with the MBIM library.
+ **/
+
+/*****************************************************************************/
+
+gchar *
+__mbim_utils_str_hex (gconstpointer mem,
+                      gsize size,
+                      gchar delimiter)
+{
+    const guint8 *data = mem;
+	gsize i;
+	gsize j;
+	gsize new_str_length;
+	gchar *new_str;
+
+	/* Get new string length. If input string has N bytes, we need:
+	 * - 1 byte for last NUL char
+	 * - 2N bytes for hexadecimal char representation of each byte...
+	 * - N-1 bytes for the separator ':'
+	 * So... a total of (1+2N+N-1) = 3N bytes are needed... */
+	new_str_length =  3 * size;
+
+	/* Allocate memory for new array and initialize contents to NUL */
+	new_str = g_malloc0 (new_str_length);
+
+	/* Print hexadecimal representation of each byte... */
+	for (i = 0, j = 0; i < size; i++, j += 3) {
+		/* Print character in output string... */
+		snprintf (&new_str[j], 3, "%02X", data[i]);
+		/* And if needed, add separator */
+		if (i != (size - 1) )
+			new_str[j + 2] = delimiter;
+	}
+
+	/* Set output string */
+	return new_str;
+}
+
+/*****************************************************************************/
+
+static volatile gint __traces_enabled = FALSE;
+
+/**
+ * mbim_utils_get_traces_enabled:
+ *
+ * Checks whether MBIM message traces are currently enabled.
+ *
+ * Returns: %TRUE if traces are enabled, %FALSE otherwise.
+ */
+gboolean
+mbim_utils_get_traces_enabled (void)
+{
+    return (gboolean) g_atomic_int_get (&__traces_enabled);
+}
+
+/**
+ * mbim_utils_set_traces_enabled:
+ * @enabled: %TRUE to enable traces, %FALSE to disable them.
+ *
+ * Sets whether MBIM message traces are enabled or disabled.
+ */
+void
+mbim_utils_set_traces_enabled (gboolean enabled)
+{
+    g_atomic_int_set (&__traces_enabled, enabled);
+}
diff --git a/libmbim-glib/mbim-utils.h b/libmbim-glib/mbim-utils.h
new file mode 100644
index 0000000..ec477e8
--- /dev/null
+++ b/libmbim-glib/mbim-utils.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@lanedo.com>
+ */
+
+#ifndef _LIBMBIM_GLIB_MBIM_UTILS_H_
+#define _LIBMBIM_GLIB_MBIM_UTILS_H_
+
+#if !defined (__LIBMBIM_GLIB_H_INSIDE__) && !defined (LIBMBIM_GLIB_COMPILATION)
+#error "Only <libmbim-glib.h> can be included directly."
+#endif
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/* Enabling/Disabling traces */
+gboolean mbim_utils_get_traces_enabled (void);
+void     mbim_utils_set_traces_enabled (gboolean enabled);
+
+/* Other private methods */
+
+#if defined (LIBMBIM_GLIB_COMPILATION)
+gchar *__mbim_utils_str_hex (gconstpointer mem,
+                             gsize         size,
+                             gchar         delimiter);
+#endif
+
+G_END_DECLS
+
+#endif /* _LIBMBIM_GLIB_MBIM_UTILS_H_ */
diff --git a/libmbim-glib/mbim-uuid.c b/libmbim-glib/mbim-uuid.c
new file mode 100644
index 0000000..7c7160e
--- /dev/null
+++ b/libmbim-glib/mbim-uuid.c
@@ -0,0 +1,373 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "mbim-uuid.h"
+
+/**
+ * SECTION: mbim-uuid
+ * @title: UUIDs
+ *
+ * This section defines the data type for unique identifiers.
+ */
+
+/*****************************************************************************/
+
+/**
+ * mbim_uuid_cmp:
+ * @a: a #MbimUuid.
+ * @b: a #MbimUuid.
+ *
+ * Compare two %MbimUuid values.
+ *
+ * Returns: %TRUE if @a and @b are equal, %FALSE otherwise.
+ */
+gboolean
+mbim_uuid_cmp (const MbimUuid *a,
+               const MbimUuid *b)
+{
+    return (memcmp (a, b, sizeof (*a)) == 0);
+}
+
+/**
+ * mbim_uuid_get_printable:
+ * @uuid: a #MbimUuid.
+ *
+ * Get a string with the UUID.
+ *
+ * Returns: (transfer full): a newly allocated string, which should be freed with g_free().
+ */
+gchar *
+mbim_uuid_get_printable (const MbimUuid *uuid)
+
+{
+    return (g_strdup_printf (
+                "%02x%02x%02x%02x-"
+                "%02x%02x-"
+                "%02x%02x-"
+                "%02x%02x-"
+                "%02x%02x%02x%02x%02x%02x",
+                uuid->a[0], uuid->a[1], uuid->a[2], uuid->a[3],
+                uuid->b[0], uuid->b[1],
+                uuid->c[0], uuid->c[1],
+                uuid->d[0], uuid->d[1],
+                uuid->e[0], uuid->e[1], uuid->e[2], uuid->e[3], uuid->e[4], uuid->e[5]));
+}
+
+/*****************************************************************************/
+
+static const MbimUuid uuid_invalid = {
+    .a = { 0x00, 0x00, 0x00, 0x00 },
+    .b = { 0x00, 0x00 },
+    .c = { 0x00, 0x00 },
+    .d = { 0x00, 0x00 },
+    .e = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+};
+
+static const MbimUuid uuid_basic_connect = {
+    .a = { 0xa2, 0x89, 0xcc, 0x33 },
+    .b = { 0xbc, 0xbb },
+    .c = { 0x8b, 0x4f },
+    .d = { 0xb6, 0xb0 },
+    .e = { 0x13, 0x3e, 0xc2, 0xaa, 0xe6, 0xdf }
+};
+
+static const MbimUuid uuid_sms = {
+    .a = { 0x53, 0x3f, 0xbe, 0xeb },
+    .b = { 0x14, 0xfe },
+    .c = { 0x44, 0x67 },
+    .d = { 0x9f, 0x90 },
+    .e = { 0x33, 0xa2, 0x23, 0xe5, 0x6c, 0x3f }
+};
+
+static const MbimUuid uuid_ussd = {
+    .a = { 0xe5, 0x50, 0xa0, 0xc8 },
+    .b = { 0x5e, 0x82 },
+    .c = { 0x47, 0x9e },
+    .d = { 0x82, 0xf7 },
+    .e = { 0x10, 0xab, 0xf4, 0xc3, 0x35, 0x1f }
+};
+
+static const MbimUuid uuid_phonebook = {
+    .a = { 0x4b, 0xf3, 0x84, 0x76 },
+    .b = { 0x1e, 0x6a },
+    .c = { 0x41, 0xdb },
+    .d = { 0xb1, 0xd8 },
+    .e = { 0xbe, 0xd2, 0x89, 0xc2, 0x5b, 0xdb }
+};
+
+static const MbimUuid uuid_stk = {
+    .a = { 0xd8, 0xf2, 0x01, 0x31 },
+    .b = { 0xfc, 0xb5 },
+    .c = { 0x4e, 0x17 },
+    .d = { 0x86, 0x02 },
+    .e = { 0xd6, 0xed, 0x38, 0x16, 0x16, 0x4c }
+};
+
+static const MbimUuid uuid_auth = {
+    .a = { 0x1d, 0x2b, 0x5f, 0xf7 },
+    .b = { 0x0a, 0xa1 },
+    .c = { 0x48, 0xb2 },
+    .d = { 0xaa, 0x52 },
+    .e = { 0x50, 0xf1, 0x57, 0x67, 0x17, 0x4e }
+};
+
+static const MbimUuid uuid_dss = {
+    .a = { 0xc0, 0x8a, 0x26, 0xdd },
+    .b = { 0x77, 0x18 },
+    .c = { 0x43, 0x82 },
+    .d = { 0x84, 0x82 },
+    .e = { 0x6e, 0x0d, 0x58, 0x3c, 0x4d, 0x0e }
+};
+
+/**
+ * mbim_uuid_from_service:
+ * @service: a #MbimService.
+ *
+ * Get the UUID corresponding to @service.
+ *
+ * Returns: (transfer none): a #MbimUuid.
+ */
+const MbimUuid *
+mbim_uuid_from_service (MbimService service)
+{
+    g_return_val_if_fail (service >= MBIM_SERVICE_INVALID && service <= MBIM_SERVICE_DSS,
+                          &uuid_invalid);
+
+    switch (service) {
+    case MBIM_SERVICE_INVALID:
+        return &uuid_invalid;
+    case MBIM_SERVICE_BASIC_CONNECT:
+        return &uuid_basic_connect;
+    case MBIM_SERVICE_SMS:
+        return &uuid_sms;
+    case MBIM_SERVICE_USSD:
+        return &uuid_ussd;
+    case MBIM_SERVICE_PHONEBOOK:
+        return &uuid_phonebook;
+    case MBIM_SERVICE_STK:
+        return &uuid_stk;
+    case MBIM_SERVICE_AUTH:
+        return &uuid_auth;
+    case MBIM_SERVICE_DSS:
+        return &uuid_dss;
+    default:
+        g_assert_not_reached ();
+    }
+}
+
+/**
+ * mbim_uuid_to_service:
+ * @uuid: a #MbimUuid.
+ *
+ * Get the service corresponding to @uuid.
+ *
+ * Returns: a #MbimService.
+ */
+MbimService
+mbim_uuid_to_service (const MbimUuid *uuid)
+{
+    if (mbim_uuid_cmp (uuid, &uuid_basic_connect))
+        return MBIM_SERVICE_BASIC_CONNECT;
+
+    if (mbim_uuid_cmp (uuid, &uuid_sms))
+        return MBIM_SERVICE_SMS;
+
+    if (mbim_uuid_cmp (uuid, &uuid_ussd))
+        return MBIM_SERVICE_USSD;
+
+    if (mbim_uuid_cmp (uuid, &uuid_phonebook))
+        return MBIM_SERVICE_PHONEBOOK;
+
+    if (mbim_uuid_cmp (uuid, &uuid_stk))
+        return MBIM_SERVICE_STK;
+
+    if (mbim_uuid_cmp (uuid, &uuid_auth))
+        return MBIM_SERVICE_AUTH;
+
+    if (mbim_uuid_cmp (uuid, &uuid_dss))
+        return MBIM_SERVICE_DSS;
+
+    return MBIM_SERVICE_INVALID;
+}
+
+/*****************************************************************************/
+
+static const MbimUuid uuid_context_type_none = {
+    .a = { 0xB4, 0x3F, 0x75, 0x8C },
+    .b = { 0xA5, 0x60 },
+    .c = { 0x4B, 0x46 },
+    .d = { 0xB3, 0x5E },
+    .e = { 0xC5, 0x86, 0x96, 0x41, 0xFB, 0x54 }
+};
+
+static const MbimUuid uuid_context_type_internet = {
+    .a = { 0x7E, 0x5E, 0x2A, 0x7E },
+    .b = { 0x4E, 0x6F },
+    .c = { 0x72, 0x72 },
+    .d = { 0x73, 0x6B },
+    .e = { 0x65, 0x6E, 0x7E, 0x5E, 0x2A, 0x7E }
+};
+
+static const MbimUuid uuid_context_type_vpn = {
+    .a = { 0x9B, 0x9F, 0x7B, 0xBE },
+    .b = { 0x89, 0x52 },
+    .c = { 0x44, 0xB7 },
+    .d = { 0x83, 0xAC },
+    .e = { 0xCA, 0x41, 0x31, 0x8D, 0xF7, 0xA0 }
+};
+
+static const MbimUuid uuid_context_type_voice = {
+    .a = { 0x88, 0x91, 0x82, 0x94 },
+    .b = { 0x0E, 0xF4 },
+    .c = { 0x43, 0x96 },
+    .d = { 0x8C, 0xCA },
+    .e = { 0xA8, 0x58, 0x8F, 0xBC, 0x02, 0xB2 }
+};
+
+static const MbimUuid uuid_context_type_video_share = {
+    .a = { 0x05, 0xA2, 0xA7, 0x16 },
+    .b = { 0x7C, 0x34 },
+    .c = { 0x4B, 0x4D },
+    .d = { 0x9A, 0x91 },
+    .e = { 0xC5, 0xEF, 0x0C, 0x7A, 0xAA, 0xCC }
+};
+
+static const MbimUuid uuid_context_type_purchase = {
+    .a = { 0xB3, 0x27, 0x24, 0x96 },
+    .b = { 0xAC, 0x6C },
+    .c = { 0x42, 0x2B },
+    .d = { 0xA8, 0xC0 },
+    .e = { 0xAC, 0xF6, 0x87, 0xA2, 0x72, 0x17 }
+};
+
+static const MbimUuid uuid_context_type_ims = {
+    .a = { 0x21, 0x61, 0x0D, 0x01 },
+    .b = { 0x30, 0x74 },
+    .c = { 0x4B, 0xCE },
+    .d = { 0x94, 0x25 },
+    .e = { 0xB5, 0x3A, 0x07, 0xD6, 0x97, 0xD6 }
+};
+
+static const MbimUuid uuid_context_type_mms = {
+    .a = { 0x46, 0x72, 0x66, 0x64 },
+    .b = { 0x72, 0x69 },
+    .c = { 0x6B, 0xC6 },
+    .d = { 0x96, 0x24 },
+    .e = { 0xD1, 0xD3, 0x53, 0x89, 0xAC, 0xA9 }
+};
+
+static const MbimUuid uuid_context_type_local = {
+    .a = { 0xA5, 0x7A, 0x9A, 0xFC },
+    .b = { 0xB0, 0x9F },
+    .c = { 0x45, 0xD7 },
+    .d = { 0xBB, 0x40 },
+    .e = { 0x03, 0x3C, 0x39, 0xF6, 0x0D, 0xB9 }
+};
+
+/**
+ * mbim_uuid_from_context_type:
+ * @context_type: a #MbimContextType.
+ *
+ * Get the UUID corresponding to @context_type.
+ *
+ * Returns: (transfer none): a #MbimUuid.
+ */
+const MbimUuid *
+mbim_uuid_from_context_type (MbimContextType context_type)
+{
+    g_return_val_if_fail (context_type >= MBIM_CONTEXT_TYPE_INVALID && context_type <= MBIM_CONTEXT_TYPE_LOCAL,
+                          &uuid_invalid);
+
+    switch (context_type) {
+    case MBIM_CONTEXT_TYPE_INVALID:
+        return &uuid_invalid;
+    case MBIM_CONTEXT_TYPE_NONE:
+        return &uuid_context_type_none;
+    case MBIM_CONTEXT_TYPE_INTERNET:
+        return &uuid_context_type_internet;
+    case MBIM_CONTEXT_TYPE_VPN:
+        return &uuid_context_type_vpn;
+    case MBIM_CONTEXT_TYPE_VOICE:
+        return &uuid_context_type_none;
+    case MBIM_CONTEXT_TYPE_VIDEO_SHARE:
+        return &uuid_context_type_video_share;
+    case MBIM_CONTEXT_TYPE_PURCHASE:
+        return &uuid_context_type_purchase;
+    case MBIM_CONTEXT_TYPE_IMS:
+        return &uuid_context_type_ims;
+    case MBIM_CONTEXT_TYPE_MMS:
+        return &uuid_context_type_mms;
+    case MBIM_CONTEXT_TYPE_LOCAL:
+        return &uuid_context_type_local;
+    default:
+        g_assert_not_reached ();
+    }
+}
+
+/**
+ * mbim_uuid_to_context_type:
+ * @uuid: a #MbimUuid.
+ *
+ * Get the context type corresponding to @uuid.
+ *
+ * Returns: a #MbimContextType.
+ */
+MbimContextType
+mbim_uuid_to_context_type (const MbimUuid *uuid)
+{
+    if (mbim_uuid_cmp (uuid, &uuid_dss))
+        return MBIM_SERVICE_DSS;
+
+    if (mbim_uuid_cmp (uuid, &uuid_context_type_none))
+        return MBIM_CONTEXT_TYPE_NONE;
+
+    if (mbim_uuid_cmp (uuid, &uuid_context_type_internet))
+        return MBIM_CONTEXT_TYPE_INTERNET;
+
+    if (mbim_uuid_cmp (uuid, &uuid_context_type_vpn))
+        return MBIM_CONTEXT_TYPE_VPN;
+
+    if (mbim_uuid_cmp (uuid, &uuid_context_type_voice))
+        return MBIM_CONTEXT_TYPE_VOICE;
+
+    if (mbim_uuid_cmp (uuid, &uuid_context_type_video_share))
+        return MBIM_CONTEXT_TYPE_VIDEO_SHARE;
+
+    if (mbim_uuid_cmp (uuid, &uuid_context_type_purchase))
+        return MBIM_CONTEXT_TYPE_PURCHASE;
+
+    if (mbim_uuid_cmp (uuid, &uuid_context_type_ims))
+        return MBIM_CONTEXT_TYPE_IMS;
+
+    if (mbim_uuid_cmp (uuid, &uuid_context_type_mms))
+        return MBIM_CONTEXT_TYPE_MMS;
+
+    if (mbim_uuid_cmp (uuid, &uuid_context_type_local))
+        return MBIM_CONTEXT_TYPE_LOCAL;
+
+    return MBIM_CONTEXT_TYPE_INVALID;
+}
diff --git a/libmbim-glib/mbim-uuid.h b/libmbim-glib/mbim-uuid.h
new file mode 100644
index 0000000..0676861
--- /dev/null
+++ b/libmbim-glib/mbim-uuid.h
@@ -0,0 +1,195 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/*
+ * libmbim-glib -- GLib/GIO based library to control MBIM devices
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#ifndef _LIBMBIM_GLIB_MBIM_UUID_H_
+#define _LIBMBIM_GLIB_MBIM_UUID_H_
+
+#if !defined (__LIBMBIM_GLIB_H_INSIDE__) && !defined (LIBMBIM_GLIB_COMPILATION)
+#error "Only <libmbim-glib.h> can be included directly."
+#endif
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/*****************************************************************************/
+
+/**
+ * MbimUuid:
+ *
+ * A UUID as defined in MBIM.
+ */
+typedef struct _MbimUuid MbimUuid;
+#define MBIM_PACKED __attribute__((__packed__))
+struct MBIM_PACKED _MbimUuid {
+    guint8 a[4];
+    guint8 b[2];
+    guint8 c[2];
+    guint8 d[2];
+    guint8 e[6];
+};
+#undef MBIM_PACKED
+
+gboolean  mbim_uuid_cmp           (const MbimUuid *a,
+                                   const MbimUuid *b);
+gchar    *mbim_uuid_get_printable (const MbimUuid *uuid);
+
+/*****************************************************************************/
+
+/**
+ * MbimService:
+ * @MBIM_SERVICE_INVALID: Invalid service.
+ * @MBIM_SERVICE_BASIC_CONNECT: Basic connectivity service.
+ * @MBIM_SERVICE_SMS: SMS messaging service.
+ * @MBIM_SERVICE_USSD: USSD service.
+ * @MBIM_SERVICE_PHONEBOOK: Phonebook service.
+ * @MBIM_SERVICE_STK: SIM toolkit service.
+ * @MBIM_SERVICE_AUTH: Authentication service.
+ * @MBIM_SERVICE_DSS: Device Service Stream service.
+ *
+ * Enumeration of the generic MBIM services.
+ */
+typedef enum {
+    MBIM_SERVICE_INVALID       = 0,
+    MBIM_SERVICE_BASIC_CONNECT = 1,
+    MBIM_SERVICE_SMS           = 2,
+    MBIM_SERVICE_USSD          = 3,
+    MBIM_SERVICE_PHONEBOOK     = 4,
+    MBIM_SERVICE_STK           = 5,
+    MBIM_SERVICE_AUTH          = 6,
+    MBIM_SERVICE_DSS           = 7,
+} MbimService;
+
+/**
+ * MBIM_UUID_INVALID:
+ *
+ * Get the UUID of the %MBIM_SERVICE_INVALID service.
+ *
+ * Returns: (transfer none): a #MbimUuid.
+ */
+#define MBIM_UUID_INVALID mbim_uuid_from_service (MBIM_SERVICE_INVALID)
+
+/**
+ * MBIM_UUID_BASIC_CONNECT:
+ *
+ * Get the UUID of the %MBIM_SERVICE_BASIC_CONNECT service.
+ *
+ * Returns: (transfer none): a #MbimUuid.
+ */
+#define MBIM_UUID_BASIC_CONNECT mbim_uuid_from_service (MBIM_SERVICE_BASIC_CONNECT)
+
+/**
+ * MBIM_UUID_SMS:
+ *
+ * Get the UUID of the %MBIM_SERVICE_SMS service.
+ *
+ * Returns: (transfer none): a #MbimUuid.
+ */
+#define MBIM_UUID_SMS mbim_uuid_from_service (MBIM_SERVICE_SMS)
+
+/**
+ * MBIM_UUID_USSD:
+ *
+ * Get the UUID of the %MBIM_SERVICE_USSD service.
+ *
+ * Returns: (transfer none): a #MbimUuid.
+ */
+#define MBIM_UUID_USSD mbim_uuid_from_service (MBIM_SERVICE_USSD)
+
+/**
+ * MBIM_UUID_PHONEBOOK:
+ *
+ * Get the UUID of the %MBIM_SERVICE_PHONEBOOK service.
+ *
+ * Returns: (transfer none): a #MbimUuid.
+ */
+#define MBIM_UUID_PHONEBOOK mbim_uuid_from_service (MBIM_SERVICE_PHONEBOOK)
+
+/**
+ * MBIM_UUID_STK:
+ *
+ * Get the UUID of the %MBIM_SERVICE_STK service.
+ *
+ * Returns: (transfer none): a #MbimUuid.
+ */
+#define MBIM_UUID_STK mbim_uuid_from_service (MBIM_SERVICE_STK)
+
+/**
+ * MBIM_UUID_AUTH:
+ *
+ * Get the UUID of the %MBIM_SERVICE_AUTH service.
+ *
+ * Returns: (transfer none): a #MbimUuid.
+ */
+#define MBIM_UUID_AUTH mbim_uuid_from_service (MBIM_SERVICE_AUTH)
+
+/**
+ * MBIM_UUID_DSS:
+ *
+ * Get the UUID of the %MBIM_SERVICE_DSS service.
+ *
+ * Returns: (transfer none): a #MbimUuid.
+ */
+#define MBIM_UUID_DSS mbim_uuid_from_service (MBIM_SERVICE_DSS)
+
+/* To/From service */
+const MbimUuid *mbim_uuid_from_service  (MbimService     service);
+MbimService     mbim_uuid_to_service    (const MbimUuid *uuid);
+
+/*****************************************************************************/
+
+/**
+ * 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.
+ *
+ * Enumeration of the generic MBIM context types.
+ */
+typedef enum {
+    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,
+} MbimContextType;
+
+/* To/From context type */
+const MbimUuid  *mbim_uuid_from_context_type (MbimContextType  context_type);
+MbimContextType  mbim_uuid_to_context_type   (const MbimUuid  *uuid);
+
+G_END_DECLS
+
+#endif /* _LIBMBIM_GLIB_MBIM_UUID_H_ */
diff --git a/libmbim-glib/mbim-version.h.in b/libmbim-glib/mbim-version.h.in
new file mode 100644
index 0000000..23debda
--- /dev/null
+++ b/libmbim-glib/mbim-version.h.in
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2013 Lanedo GmbH
+ */
+
+#ifndef _MBIM_VERSION_H_
+#define _MBIM_VERSION_H_
+
+/**
+ * SECTION:mbim-version
+ * @short_description: Version information in the API.
+ *
+ * This section defines types that are used to identify the libmbim-glib version.
+ **/
+
+/**
+ * MBIM_MAJOR_VERSION:
+ *
+ * Evaluates to the major version number of libmbim-glib which this source
+ * is compiled against.
+ */
+#define MBIM_MAJOR_VERSION (@MBIM_MAJOR_VERSION@)
+
+/**
+ * MBIM_MINOR_VERSION:
+ *
+ * Evaluates to the minor version number of libmbim-glib which this source
+ * is compiled against.
+ */
+#define MBIM_MINOR_VERSION (@MBIM_MINOR_VERSION@)
+
+/**
+ * MBIM_MICRO_VERSION:
+ *
+ * Evaluates to the micro version number of libmbim-glib which this source
+ * compiled against.
+ */
+#define MBIM_MICRO_VERSION (@MBIM_MICRO_VERSION@)
+
+/**
+ * MBIM_CHECK_VERSION:
+ * @major: major version (e.g. 1 for version 1.2.5)
+ * @minor: minor version (e.g. 2 for version 1.2.5)
+ * @micro: micro version (e.g. 5 for version 1.2.5)
+ *
+ * Returns: %TRUE if the version of the libmbim-glib header files
+ * is the same as or newer than the passed-in version.
+ */
+#define MBIM_CHECK_VERSION(major,minor,micro)                         \
+    (MBIM_MAJOR_VERSION > (major) ||                                  \
+     (MBIM_MAJOR_VERSION == (major) && MBIM_MINOR_VERSION > (minor)) || \
+     (MBIM_MAJOR_VERSION == (major) && MBIM_MINOR_VERSION == (minor) && MBIM_MICRO_VERSION >= (micro)))
+
+#endif /* _MBIM_VERSION_H_ */
diff --git a/libmbim-glib/test/Makefile.am b/libmbim-glib/test/Makefile.am
new file mode 100644
index 0000000..4177491
--- /dev/null
+++ b/libmbim-glib/test/Makefile.am
@@ -0,0 +1,86 @@
+include $(top_srcdir)/gtester.make
+
+noinst_PROGRAMS = \
+	test-uuid \
+	test-cid \
+	test-message \
+	test-fragment \
+	test-message-parser \
+	test-message-builder
+
+TEST_PROGS += $(noinst_PROGRAMS)
+
+
+test_uuid_SOURCES = \
+	test-uuid.c
+test_uuid_CPPFLAGS = \
+	$(LIBMBIM_GLIB_CFLAGS) \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/libmbim-glib \
+	-I$(top_builddir)/libmbim-glib \
+	-DLIBMBIM_GLIB_COMPILATION
+test_uuid_LDADD = \
+	$(top_builddir)/libmbim-glib/libmbim-glib.la \
+	$(LIBMBIM_GLIB_LIBS)
+
+test_cid_SOURCES = \
+	test-cid.c
+test_cid_CPPFLAGS = \
+	$(LIBMBIM_GLIB_CFLAGS) \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/libmbim-glib \
+	-I$(top_builddir)/libmbim-glib \
+	-DLIBMBIM_GLIB_COMPILATION
+test_cid_LDADD = \
+	$(top_builddir)/libmbim-glib/libmbim-glib.la \
+	$(LIBMBIM_GLIB_LIBS)
+
+test_message_SOURCES = \
+	test-message.c
+test_message_CPPFLAGS = \
+	$(LIBMBIM_GLIB_CFLAGS) \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/libmbim-glib \
+	-I$(top_builddir)/libmbim-glib \
+	-DLIBMBIM_GLIB_COMPILATION
+test_message_LDADD = \
+	$(top_builddir)/libmbim-glib/libmbim-glib.la \
+	$(LIBMBIM_GLIB_LIBS)
+
+test_fragment_SOURCES = \
+	test-fragment.c
+test_fragment_CPPFLAGS = \
+	$(LIBMBIM_GLIB_CFLAGS) \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/libmbim-glib \
+	-I$(top_builddir)/libmbim-glib \
+	-DLIBMBIM_GLIB_COMPILATION
+test_fragment_LDADD = \
+	$(top_builddir)/libmbim-glib/libmbim-glib.la \
+	$(LIBMBIM_GLIB_LIBS)
+
+test_message_parser_SOURCES = \
+	test-message-parser.c
+test_message_parser_CPPFLAGS = \
+	$(LIBMBIM_GLIB_CFLAGS) \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/libmbim-glib \
+	-I$(top_builddir)/libmbim-glib \
+	-I$(top_builddir)/libmbim-glib/generated \
+	-DLIBMBIM_GLIB_COMPILATION
+test_message_parser_LDADD = \
+	$(top_builddir)/libmbim-glib/libmbim-glib.la \
+	$(LIBMBIM_GLIB_LIBS)
+
+test_message_builder_SOURCES = \
+	test-message-builder.c
+test_message_builder_CPPFLAGS = \
+	$(LIBMBIM_GLIB_CFLAGS) \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/libmbim-glib \
+	-I$(top_builddir)/libmbim-glib \
+	-I$(top_builddir)/libmbim-glib/generated \
+	-DLIBMBIM_GLIB_COMPILATION
+test_message_builder_LDADD = \
+	$(top_builddir)/libmbim-glib/libmbim-glib.la \
+	$(LIBMBIM_GLIB_LIBS)
diff --git a/libmbim-glib/test/test-cid.c b/libmbim-glib/test/test-cid.c
new file mode 100644
index 0000000..ffb6837
--- /dev/null
+++ b/libmbim-glib/test/test-cid.c
@@ -0,0 +1,121 @@
+/* -*- Mode: C; 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 the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include <config.h>
+
+#include "mbim-cid.h"
+
+static void
+test_common (MbimService service,
+             guint       cid,
+             gboolean    can_set,
+             gboolean    can_query,
+             gboolean    can_notify)
+{
+    g_assert (mbim_cid_can_set    (service, cid) == can_set);
+    g_assert (mbim_cid_can_query  (service, cid) == can_query);
+    g_assert (mbim_cid_can_notify (service, cid) == can_notify);
+}
+
+static void
+test_cid_basic_connect (void)
+{
+    test_common (MBIM_SERVICE_BASIC_CONNECT,
+                 MBIM_CID_BASIC_CONNECT_DEVICE_CAPS,
+                 FALSE, TRUE, FALSE);
+
+    test_common (MBIM_SERVICE_BASIC_CONNECT,
+                 MBIM_CID_BASIC_CONNECT_MULTICARRIER_PROVIDERS,
+                 TRUE, TRUE, TRUE);
+}
+
+static void
+test_cid_sms (void)
+{
+    test_common (MBIM_SERVICE_SMS,
+                 MBIM_CID_SMS_CONFIGURATION,
+                 TRUE, TRUE, TRUE);
+
+    test_common (MBIM_SERVICE_SMS,
+                 MBIM_CID_SMS_MESSAGE_STORE_STATUS,
+                 FALSE, TRUE, TRUE);
+}
+
+static void
+test_cid_ussd (void)
+{
+    test_common (MBIM_SERVICE_USSD,
+                 MBIM_CID_USSD,
+                 TRUE, FALSE, TRUE);
+}
+
+static void
+test_cid_phonebook (void)
+{
+    test_common (MBIM_SERVICE_PHONEBOOK,
+                 MBIM_CID_PHONEBOOK_CONFIGURATION,
+                 FALSE, TRUE, TRUE);
+
+    test_common (MBIM_SERVICE_PHONEBOOK,
+                 MBIM_CID_PHONEBOOK_WRITE,
+                 TRUE, FALSE, FALSE);
+}
+
+static void
+test_cid_stk (void)
+{
+    test_common (MBIM_SERVICE_STK,
+                 MBIM_CID_STK_PAC,
+                 TRUE, TRUE, TRUE);
+
+    test_common (MBIM_SERVICE_STK,
+                 MBIM_CID_STK_ENVELOPE,
+                 TRUE, TRUE, FALSE);
+}
+
+static void
+test_cid_auth (void)
+{
+    test_common (MBIM_SERVICE_AUTH,
+                 MBIM_CID_AUTH_AKA,
+                 FALSE, TRUE, FALSE);
+
+    test_common (MBIM_SERVICE_AUTH,
+                 MBIM_CID_AUTH_SIM,
+                 FALSE, TRUE, FALSE);
+}
+
+static void
+test_cid_dss (void)
+{
+    test_common (MBIM_SERVICE_DSS,
+                 MBIM_CID_DSS_CONNECT,
+                 TRUE, FALSE, FALSE);
+}
+
+int main (int argc, char **argv)
+{
+    g_test_init (&argc, &argv, NULL);
+
+    g_test_add_func ("/libmbim-glib/cid/basic-connect", test_cid_basic_connect);
+    g_test_add_func ("/libmbim-glib/cid/sms",           test_cid_sms);
+    g_test_add_func ("/libmbim-glib/cid/ussd",          test_cid_ussd);
+    g_test_add_func ("/libmbim-glib/cid/phonebook",     test_cid_phonebook);
+    g_test_add_func ("/libmbim-glib/cid/stk",           test_cid_stk);
+    g_test_add_func ("/libmbim-glib/cid/auth",          test_cid_auth);
+    g_test_add_func ("/libmbim-glib/cid/dss",           test_cid_dss);
+
+    return g_test_run ();
+}
diff --git a/libmbim-glib/test/test-fragment.c b/libmbim-glib/test/test-fragment.c
new file mode 100644
index 0000000..15f9df9
--- /dev/null
+++ b/libmbim-glib/test/test-fragment.c
@@ -0,0 +1,331 @@
+/* -*- Mode: C; 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 the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "mbim-message.h"
+#include "mbim-message-private.h"
+
+static void
+test_fragment_receive_single (void)
+{
+    MbimMessage *message;
+    const guint8 *fragment_information_buffer;
+    guint32 fragment_information_buffer_length;
+
+    /* This buffer contains a single message composed of a single fragment.
+     * We don't really care about the actual data included within the fragment. */
+    const guint8 buffer [] =  {
+        0x07, 0x00, 0x00, 0x80, /* indications have fragments */
+        0x1C, 0x00, 0x00, 0x00, /* length of this fragment */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        0x01, 0x00, 0x00, 0x00, /* total fragments */
+        0x00, 0x00, 0x00, 0x00, /* current fragment */
+        0x01, 0x02, 0x03, 0x04, /* frament data */
+        0x05, 0x06, 0x07, 0x08 };
+
+    const guint8 data [] = {
+        0x01, 0x02, 0x03, 0x04, /* same data as in the fragment */
+        0x05, 0x06, 0x07, 0x08
+    };
+
+    message = mbim_message_new (buffer, sizeof (buffer));
+    g_assert         (_mbim_message_is_fragment          (message));
+    g_assert_cmpuint (_mbim_message_fragment_get_total   (message), ==, 1);
+    g_assert_cmpuint (_mbim_message_fragment_get_current (message), ==, 0);
+
+    fragment_information_buffer = (_mbim_message_fragment_get_payload (
+                                       message,
+                                       &fragment_information_buffer_length));
+
+    g_assert_cmpuint (fragment_information_buffer_length, ==, sizeof (data));
+    g_assert (memcmp (fragment_information_buffer, data, fragment_information_buffer_length) == 0);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_fragment_receive_multiple (void)
+{
+    GByteArray *bytearray;
+    MbimMessage *message;
+    GError *error = NULL;
+    const guint8 *fragment_information_buffer;
+    guint32 fragment_information_buffer_length;
+
+    /* This buffer contains several fragments of a single message.
+     * We don't really care about the actual data included within the fragments. */
+    const guint8 buffer [] =  {
+        /* First fragment */
+        0x07, 0x00, 0x00, 0x80, /* indications have fragments */
+        0x1C, 0x00, 0x00, 0x00, /* length of this fragment */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        0x04, 0x00, 0x00, 0x00, /* total fragments */
+        0x00, 0x00, 0x00, 0x00, /* current fragment */
+        0x00, 0x01, 0x02, 0x03, /* frament data */
+        0x04, 0x05, 0x06, 0x07,
+        /* Second fragment */
+        0x07, 0x00, 0x00, 0x80, /* indications have fragments */
+        0x1C, 0x00, 0x00, 0x00, /* length of this fragment */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        0x04, 0x00, 0x00, 0x00, /* total fragments */
+        0x01, 0x00, 0x00, 0x00, /* current fragment */
+        0x08, 0x09, 0x0A, 0x0B, /* frament data */
+        0x0C, 0x0D, 0x0E, 0x0F,
+        /* Third fragment */
+        0x07, 0x00, 0x00, 0x80, /* indications have fragments */
+        0x1C, 0x00, 0x00, 0x00, /* length of this fragment */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        0x04, 0x00, 0x00, 0x00, /* total fragments */
+        0x02, 0x00, 0x00, 0x00, /* current fragment */
+        0x10, 0x11, 0x12, 0x13, /* frament data */
+        0x14, 0x15, 0x16, 0x17,
+        /* Fourth fragment */
+        0x07, 0x00, 0x00, 0x80, /* indications have fragments */
+        0x18, 0x00, 0x00, 0x00, /* length of this fragment */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        0x04, 0x00, 0x00, 0x00, /* total fragments */
+        0x03, 0x00, 0x00, 0x00, /* current fragment */
+        0x18, 0x19, 0x1A, 0x1B  /* frament data */
+    };
+
+    const guint8 data [] = {
+        0x00, 0x01, 0x02, 0x03, /* same data as in the fragments */
+        0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B,
+        0x0C, 0x0D, 0x0E, 0x0F,
+        0x10, 0x11, 0x12, 0x13,
+        0x14, 0x15, 0x16, 0x17,
+        0x18, 0x19, 0x1A, 0x1B
+    };
+
+    bytearray = g_byte_array_new ();
+    g_byte_array_append (bytearray, buffer, sizeof (buffer));
+
+    /* First fragment creates the message */
+    message = _mbim_message_fragment_collector_init ((const MbimMessage *)bytearray, &error);
+    g_assert_no_error (error);
+    g_assert_cmpuint (_mbim_message_fragment_get_total   (message), ==, 4);
+    g_assert_cmpuint (_mbim_message_fragment_get_current (message), ==, 0);
+    g_assert         (_mbim_message_fragment_collector_complete (message) == FALSE);
+    g_byte_array_remove_range (bytearray, 0, mbim_message_get_message_length ((const MbimMessage *)bytearray));
+
+
+    /* Add second fragment */
+    g_assert (_mbim_message_fragment_collector_add (message, (const MbimMessage *)bytearray, &error));
+    g_assert_no_error (error);
+    g_assert_cmpuint (_mbim_message_fragment_get_total   (message), ==, 4);
+    g_assert_cmpuint (_mbim_message_fragment_get_current (message), ==, 1);
+    g_assert         (_mbim_message_fragment_collector_complete (message) == FALSE);
+    g_byte_array_remove_range (bytearray, 0, mbim_message_get_message_length ((const MbimMessage *)bytearray));
+
+    /* Add third fragment */
+    g_assert (_mbim_message_fragment_collector_add (message, (const MbimMessage *)bytearray, &error));
+    g_assert_no_error (error);
+    g_assert_cmpuint (_mbim_message_fragment_get_total   (message), ==, 4);
+    g_assert_cmpuint (_mbim_message_fragment_get_current (message), ==, 2);
+    g_assert         (_mbim_message_fragment_collector_complete (message) == FALSE);
+    g_byte_array_remove_range (bytearray, 0, mbim_message_get_message_length ((const MbimMessage *)bytearray));
+
+    /* Add fourth fragment */
+    g_assert (_mbim_message_fragment_collector_add (message, (const MbimMessage *)bytearray, &error));
+    g_assert_no_error (error);
+    g_assert_cmpuint (_mbim_message_fragment_get_total   (message), ==, 4);
+    g_assert_cmpuint (_mbim_message_fragment_get_current (message), ==, 3);
+    g_assert         (_mbim_message_fragment_collector_complete (message) == TRUE);
+    g_assert_cmpuint (_mbim_message_fragment_get_total   (message), ==, 1);
+    g_assert_cmpuint (_mbim_message_fragment_get_current (message), ==, 0);
+    g_byte_array_remove_range (bytearray, 0, mbim_message_get_message_length ((const MbimMessage *)bytearray));
+
+    /* Compare all compiled data */
+    fragment_information_buffer = (_mbim_message_fragment_get_payload (
+                                       message,
+                                       &fragment_information_buffer_length));
+    g_assert_cmpuint (fragment_information_buffer_length, ==, sizeof (data));
+    g_assert (memcmp (fragment_information_buffer, data, fragment_information_buffer_length) == 0);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_fragment_send_multiple_common (guint32       max_fragment_size,
+                                    const guint8 *buffer,
+                                    gsize         buffer_length,
+                                    const guint8 *expected_buffer,
+                                    gsize         expected_buffer_length)
+{
+    GByteArray *bytearray;
+    struct fragment_info *fragments;
+    guint n_fragments;
+    guint i;
+    GByteArray *output_bytearray;
+
+    /* Setup message and split it into fragments */
+    bytearray = g_byte_array_new ();
+    g_byte_array_append (bytearray, buffer, buffer_length);
+    fragments = _mbim_message_split_fragments ((const MbimMessage *)bytearray,
+                                               max_fragment_size,
+                                               &n_fragments);
+    g_assert (fragments != NULL);
+
+    /* Simulate that we write the fragments */
+    output_bytearray = g_byte_array_new ();
+    for (i = 0; i < n_fragments; i++) {
+        /* Header */
+        g_byte_array_append (output_bytearray,
+                             (guint8 *)&fragments[i].header,
+                             sizeof (fragments[i].header));
+        /* Fragment header */
+        g_byte_array_append (output_bytearray,
+                             (guint8 *)&fragments[i].fragment_header,
+                             sizeof (fragments[i].fragment_header));
+        /* Data */
+        g_byte_array_append (output_bytearray,
+                             fragments[i].data,
+                             fragments[i].data_length);
+    }
+    g_free (fragments);
+
+    /* Compare with the expected buffer */
+    g_assert_cmpuint (expected_buffer_length, == , output_bytearray->len);
+    g_assert (memcmp (output_bytearray->data, expected_buffer, output_bytearray->len) == 0);
+
+    g_byte_array_unref (output_bytearray);
+}
+
+static void
+test_fragment_send_multiple_1 (void)
+{
+    const guint8 buffer [] = {
+        0x07, 0x00, 0x00, 0x80, /* indications have fragments */
+        0x30, 0x00, 0x00, 0x00, /* length of this fragment */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        0x01, 0x00, 0x00, 0x00, /* total fragments */
+        0x00, 0x00, 0x00, 0x00, /* current fragment */
+        0x00, 0x01, 0x02, 0x03, /* same data as in the fragments */
+        0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B,
+        0x0C, 0x0D, 0x0E, 0x0F,
+        0x10, 0x11, 0x12, 0x13,
+        0x14, 0x15, 0x16, 0x17,
+        0x18, 0x19, 0x1A, 0x1B
+    };
+
+    /* This buffer contains several fragments of a single message.
+     * We don't really care about the actual data included within the fragments. */
+    const guint8 expected_buffer [] = {
+        /* First fragment */
+        0x07, 0x00, 0x00, 0x80, /* indications have fragments */
+        0x1C, 0x00, 0x00, 0x00, /* length of this fragment */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        0x04, 0x00, 0x00, 0x00, /* total fragments */
+        0x00, 0x00, 0x00, 0x00, /* current fragment */
+        0x00, 0x01, 0x02, 0x03, /* frament data */
+        0x04, 0x05, 0x06, 0x07,
+        /* Second fragment */
+        0x07, 0x00, 0x00, 0x80, /* indications have fragments */
+        0x1C, 0x00, 0x00, 0x00, /* length of this fragment */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        0x04, 0x00, 0x00, 0x00, /* total fragments */
+        0x01, 0x00, 0x00, 0x00, /* current fragment */
+        0x08, 0x09, 0x0A, 0x0B, /* frament data */
+        0x0C, 0x0D, 0x0E, 0x0F,
+        /* Third fragment */
+        0x07, 0x00, 0x00, 0x80, /* indications have fragments */
+        0x1C, 0x00, 0x00, 0x00, /* length of this fragment */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        0x04, 0x00, 0x00, 0x00, /* total fragments */
+        0x02, 0x00, 0x00, 0x00, /* current fragment */
+        0x10, 0x11, 0x12, 0x13, /* frament data */
+        0x14, 0x15, 0x16, 0x17,
+        /* Fourth fragment */
+        0x07, 0x00, 0x00, 0x80, /* indications have fragments */
+        0x18, 0x00, 0x00, 0x00, /* length of this fragment */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        0x04, 0x00, 0x00, 0x00, /* total fragments */
+        0x03, 0x00, 0x00, 0x00, /* current fragment */
+        0x18, 0x19, 0x1A, 0x1B  /* frament data */
+    };
+
+    test_fragment_send_multiple_common (
+        28,
+        buffer, sizeof (buffer),
+        expected_buffer, sizeof (expected_buffer));
+}
+
+static void
+test_fragment_send_multiple_2 (void)
+{
+    const guint8 buffer [] = {
+        0x07, 0x00, 0x00, 0x80, /* indications have fragments */
+        0x2C, 0x00, 0x00, 0x00, /* length of this fragment */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        0x01, 0x00, 0x00, 0x00, /* total fragments */
+        0x00, 0x00, 0x00, 0x00, /* current fragment */
+        0x00, 0x01, 0x02, 0x03, /* same data as in the fragments */
+        0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B,
+        0x0C, 0x0D, 0x0E, 0x0F,
+        0x10, 0x11, 0x12, 0x13,
+        0x14, 0x15, 0x16, 0x17
+    };
+
+    /* This buffer contains several fragments of a single message.
+     * We don't really care about the actual data included within the fragments. */
+    const guint8 expected_buffer [] = {
+        /* First fragment */
+        0x07, 0x00, 0x00, 0x80, /* indications have fragments */
+        0x1C, 0x00, 0x00, 0x00, /* length of this fragment */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        0x03, 0x00, 0x00, 0x00, /* total fragments */
+        0x00, 0x00, 0x00, 0x00, /* current fragment */
+        0x00, 0x01, 0x02, 0x03, /* frament data */
+        0x04, 0x05, 0x06, 0x07,
+        /* Second fragment */
+        0x07, 0x00, 0x00, 0x80, /* indications have fragments */
+        0x1C, 0x00, 0x00, 0x00, /* length of this fragment */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        0x03, 0x00, 0x00, 0x00, /* total fragments */
+        0x01, 0x00, 0x00, 0x00, /* current fragment */
+        0x08, 0x09, 0x0A, 0x0B, /* frament data */
+        0x0C, 0x0D, 0x0E, 0x0F,
+        /* Third fragment */
+        0x07, 0x00, 0x00, 0x80, /* indications have fragments */
+        0x1C, 0x00, 0x00, 0x00, /* length of this fragment */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        0x03, 0x00, 0x00, 0x00, /* total fragments */
+        0x02, 0x00, 0x00, 0x00, /* current fragment */
+        0x10, 0x11, 0x12, 0x13, /* frament data */
+        0x14, 0x15, 0x16, 0x17
+    };
+
+    test_fragment_send_multiple_common (
+        28,
+        buffer, sizeof (buffer),
+        expected_buffer, sizeof (expected_buffer));
+}
+
+int main (int argc, char **argv)
+{
+    g_test_init (&argc, &argv, NULL);
+
+    g_test_add_func ("/libmbim-glib/fragment/receive/single",   test_fragment_receive_single);
+    g_test_add_func ("/libmbim-glib/fragment/receive/multiple", test_fragment_receive_multiple);
+    g_test_add_func ("/libmbim-glib/fragment/send/multiple-1",  test_fragment_send_multiple_1);
+    g_test_add_func ("/libmbim-glib/fragment/send/multiple-2",  test_fragment_send_multiple_2);
+
+    return g_test_run ();
+}
diff --git a/libmbim-glib/test/test-message-builder.c b/libmbim-glib/test/test-message-builder.c
new file mode 100644
index 0000000..239a8bf
--- /dev/null
+++ b/libmbim-glib/test/test-message-builder.c
@@ -0,0 +1,1321 @@
+/* -*- Mode: C; 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 the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "mbim-message.h"
+#include "mbim-message-private.h"
+#include "mbim-cid.h"
+#include "mbim-enums.h"
+#include "mbim-utils.h"
+#include "mbim-basic-connect.h"
+#include "mbim-ussd.h"
+#include "mbim-auth.h"
+#include "mbim-stk.h"
+#include "mbim-dss.h"
+
+#if defined ENABLE_TEST_MESSAGE_TRACES
+static void
+test_message_trace (const guint8 *computed,
+                    guint32       computed_size,
+                    const guint8 *expected,
+                    guint32       expected_size)
+{
+    gchar *message_str;
+    gchar *expected_str;
+
+    message_str = __mbim_utils_str_hex (computed, computed_size, ':');
+    expected_str = __mbim_utils_str_hex (expected, expected_size, ':');
+
+    /* Dump all message contents */
+    g_print ("\n"
+             "Message str:\n"
+             "'%s'\n"
+             "Expected str:\n"
+             "'%s'\n",
+             message_str,
+             expected_str);
+
+    /* If they are different, tell which are the different bytes */
+    if (computed_size != expected_size ||
+        memcmp (computed, expected, expected_size)) {
+        guint32 i;
+
+        for (i = 0; i < MIN (computed_size, expected_size); i++) {
+            if (computed[i] != expected[i])
+                g_print ("Byte [%u] is different (computed: 0x%02X vs expected: 0x%02x)\n", i, computed[i], expected[i]);
+        }
+    }
+
+    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)
+{
+    MbimMessage *message;
+    MbimMessageCommandBuilder *builder;
+    const guint8 expected_message [] = {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x50, 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,
+        0x04, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x20, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x02, 0x00, 0x00, 0x00, /* pin type */
+        0x00, 0x00, 0x00, 0x00, /* pin operation */
+        0x18, 0x00, 0x00, 0x00, /* pin offset */
+        0x08, 0x00, 0x00, 0x00, /* pin size */
+        0x00, 0x00, 0x00, 0x00, /* new pin offset */
+        0x00, 0x00, 0x00, 0x00, /* new pin size */
+        0x31, 0x00, 0x31, 0x00, /* pin string */
+        0x31, 0x00, 0x31, 0x00
+    };
+
+    /* PIN set message */
+    builder = _mbim_message_command_builder_new (1,
+                                                 MBIM_SERVICE_BASIC_CONNECT,
+                                                 MBIM_CID_BASIC_CONNECT_PIN,
+                                                 MBIM_MESSAGE_COMMAND_TYPE_SET);
+    _mbim_message_command_builder_append_guint32 (builder, (guint32)MBIM_PIN_TYPE_PIN1);
+    _mbim_message_command_builder_append_guint32 (builder, (guint32)MBIM_PIN_OPERATION_ENTER);
+    _mbim_message_command_builder_append_string  (builder, "1111");
+    _mbim_message_command_builder_append_string  (builder, "");
+    message = _mbim_message_command_builder_complete (builder);
+
+    g_assert (message != NULL);
+
+    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_PIN);
+    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);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_builder_basic_connect_pin_set (void)
+{
+    GError *error = NULL;
+    MbimMessage *message;
+    const guint8 expected_message [] = {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x50, 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,
+        0x04, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x20, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x02, 0x00, 0x00, 0x00, /* pin type */
+        0x00, 0x00, 0x00, 0x00, /* pin operation */
+        0x18, 0x00, 0x00, 0x00, /* pin offset */
+        0x08, 0x00, 0x00, 0x00, /* pin size */
+        0x00, 0x00, 0x00, 0x00, /* new pin offset */
+        0x00, 0x00, 0x00, 0x00, /* new pin size */
+        0x31, 0x00, 0x31, 0x00, /* pin string */
+        0x31, 0x00, 0x31, 0x00
+    };
+
+    /* PIN set message */
+    message = mbim_message_pin_set_new (MBIM_PIN_TYPE_PIN1,
+                                        MBIM_PIN_OPERATION_ENTER,
+                                        "1111",
+                                        "",
+                                        &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_PIN);
+    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);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_builder_basic_connect_connect_set_raw (void)
+{
+    MbimMessage *message;
+    MbimMessageCommandBuilder *builder;
+    const guint8 expected_message [] = {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x7C, 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 */
+        0x4C, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x01, 0x00, 0x00, 0x00, /* session id */
+        0x01, 0x00, 0x00, 0x00, /* activation command */
+        0x3C, 0x00, 0x00, 0x00, /* access string offset */
+        0x10, 0x00, 0x00, 0x00, /* access string size */
+        0x00, 0x00, 0x00, 0x00, /* username offset */
+        0x00, 0x00, 0x00, 0x00, /* username size */
+        0x00, 0x00, 0x00, 0x00, /* password offset */
+        0x00, 0x00, 0x00, 0x00, /* password size */
+        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,
+        /* data buffer */
+        0x69, 0x00, 0x6E, 0x00, /* access string */
+        0x74, 0x00, 0x65, 0x00,
+        0x72, 0x00, 0x6E, 0x00,
+        0x65, 0x00, 0x74, 0x00
+    };
+
+    /* CONNECT set message */
+    builder = _mbim_message_command_builder_new (1,
+                                                 MBIM_SERVICE_BASIC_CONNECT,
+                                                 MBIM_CID_BASIC_CONNECT_CONNECT,
+                                                 MBIM_MESSAGE_COMMAND_TYPE_SET);
+    _mbim_message_command_builder_append_guint32 (builder, 0x01);
+    _mbim_message_command_builder_append_guint32 (builder, (guint32)MBIM_ACTIVATION_COMMAND_ACTIVATE);
+    _mbim_message_command_builder_append_string  (builder, "internet");
+    _mbim_message_command_builder_append_string  (builder, "");
+    _mbim_message_command_builder_append_string  (builder, "");
+    _mbim_message_command_builder_append_guint32 (builder, (guint32)MBIM_COMPRESSION_NONE);
+    _mbim_message_command_builder_append_guint32 (builder, (guint32)MBIM_AUTH_PROTOCOL_PAP);
+    _mbim_message_command_builder_append_guint32 (builder, (guint32)MBIM_CONTEXT_IP_TYPE_IPV4);
+    _mbim_message_command_builder_append_uuid    (builder, mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET));
+    message = _mbim_message_command_builder_complete (builder);
+
+    g_assert (message != NULL);
+
+    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);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_builder_basic_connect_connect_set (void)
+{
+    GError *error = NULL;
+    MbimMessage *message;
+    const guint8 expected_message [] = {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x7C, 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 */
+        0x4C, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x01, 0x00, 0x00, 0x00, /* session id */
+        0x01, 0x00, 0x00, 0x00, /* activation command */
+        0x3C, 0x00, 0x00, 0x00, /* access string offset */
+        0x10, 0x00, 0x00, 0x00, /* access string size */
+        0x00, 0x00, 0x00, 0x00, /* username offset */
+        0x00, 0x00, 0x00, 0x00, /* username size */
+        0x00, 0x00, 0x00, 0x00, /* password offset */
+        0x00, 0x00, 0x00, 0x00, /* password size */
+        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,
+        /* data buffer */
+        0x69, 0x00, 0x6E, 0x00, /* access string */
+        0x74, 0x00, 0x65, 0x00,
+        0x72, 0x00, 0x6E, 0x00,
+        0x65, 0x00, 0x74, 0x00
+    };
+
+    /* CONNECT set message */
+    message = (mbim_message_connect_set_new (
+                   0x01,
+                   MBIM_ACTIVATION_COMMAND_ACTIVATE,
+                   "internet",
+                   "",
+                   "",
+                   MBIM_COMPRESSION_NONE,
+                   MBIM_AUTH_PROTOCOL_PAP,
+                   MBIM_CONTEXT_IP_TYPE_IPV4,
+                   mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
+                   &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);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_builder_basic_connect_service_activation_set (void)
+{
+    GError *error = NULL;
+    MbimMessage *message;
+    const guint8 buffer [] = {
+        0x01, 0x02, 0x03, 0x04,
+        0x05, 0x06, 0x07, 0x08
+    };
+    const guint8 expected_message [] = {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x38, 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,
+        0x0E, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x08, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x01, 0x02, 0x03, 0x04,
+        0x05, 0x06, 0x07, 0x08 };
+
+    /* CONNECT set message */
+    message = (mbim_message_service_activation_set_new (
+                   sizeof (buffer),
+                   buffer,
+                   &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_SERVICE_ACTIVATION);
+    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);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_builder_basic_connect_device_service_subscriber_list_set (void)
+{
+    GError *error = NULL;
+    MbimEventEntry **entries;
+    MbimMessage *message;
+    const guint8 expected_message [] = {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x78, 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,
+        0x13, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x48, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x02, 0x00, 0x00, 0x00, /* 0x00 Events count */
+        0x14, 0x00, 0x00, 0x00, /* 0x04 Event 1 offset */
+        0x1C, 0x00, 0x00, 0x00, /* 0x08 Event 1 length */
+        0x30, 0x00, 0x00, 0x00, /* 0x0C Event 2 offset */
+        0x18, 0x00, 0x00, 0x00, /* 0x10 Event 2 length */
+        /* data buffer, event 1 */
+        0xA2, 0x89, 0xCC, 0x33, /* 0x14 Event 1, service id */
+        0xBC, 0xBB, 0x8B, 0x4F,
+        0xB6, 0xB0, 0x13, 0x3E,
+        0xC2, 0xAA, 0xE6, 0xDF,
+        0x02, 0x00, 0x00, 0x00, /* 0x24 Event 1, cid count */
+        0x0B, 0x00, 0x00, 0x00, /* 0x28 Event 1, cid 1 */
+        0x09, 0x00, 0x00, 0x00, /* 0x2C Event 1, cid 2 */
+        /* data buffer, event 2 */
+        0x53, 0x3F, 0xBE, 0xEB, /* 0x30 Event 1, service id */
+        0x14, 0xFE, 0x44, 0x67,
+        0x9F, 0x90, 0x33, 0xA2,
+        0x23, 0xE5, 0x6C, 0x3F,
+        0x01, 0x00, 0x00, 0x00, /* 0x40 Event 2, cid count */
+        0x02, 0x00, 0x00, 0x00, /* 0x44 Event 2, cid 1 */
+    };
+
+    /* Device Service Subscribe List set message */
+    entries = g_new0 (MbimEventEntry *, 3);
+
+    entries[0] = g_new (MbimEventEntry, 1);
+    memcpy (&(entries[0]->device_service_id), MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
+    entries[0]->cids_count = 2;
+    entries[0]->cids = g_new0 (guint32, 2);
+    entries[0]->cids[0] = MBIM_CID_BASIC_CONNECT_SIGNAL_STATE;
+    entries[0]->cids[1] = MBIM_CID_BASIC_CONNECT_REGISTER_STATE;
+
+    entries[1] = g_new (MbimEventEntry, 1);
+    memcpy (&(entries[1]->device_service_id), MBIM_UUID_SMS, sizeof (MbimUuid));
+    entries[1]->cids_count = 1;
+    entries[1]->cids = g_new0 (guint32, 1);
+    entries[1]->cids[0] = MBIM_CID_SMS_READ;
+
+    message = (mbim_message_device_service_subscriber_list_set_new (
+                   2,
+                   (const MbimEventEntry *const *)entries,
+                   &error));
+    mbim_event_entry_array_free (entries);
+
+    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_DEVICE_SERVICE_SUBSCRIBER_LIST);
+    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);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_builder_ussd_set (void)
+{
+    GError *error = NULL;
+    MbimMessage *message;
+    const guint8 expected_message [] = {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x50, 0x00, 0x00, 0x00, /* length */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_message */
+        0xE5, 0x50, 0xA0, 0xC8, /* service id */
+        0x5E, 0x82, 0x47, 0x9E,
+        0x82, 0xF7, 0x10, 0xAB,
+        0xF4, 0xC3, 0x35, 0x1F,
+        0x01, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x20, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x01, 0x00, 0x00, 0x00, /* 0x00 Action */
+        0x01, 0x00, 0x00, 0x00, /* 0x04 Data coding scheme */
+        0x10, 0x00, 0x00, 0x00, /* 0x08 Payload offset */
+        0x10, 0x00, 0x00, 0x00, /* 0x0C Payload length */
+        /* data buffer, payload */
+        0x01, 0x02, 0x03, 0x04, /* 0x10 Payload */
+        0x05, 0x06, 0x07, 0x08,
+        0x09, 0x0A, 0x0B, 0x0C,
+        0x0D, 0x0E, 0x0F, 0x00
+    };
+    const guint8 payload [] = {
+        0x01, 0x02, 0x03, 0x04,
+        0x05, 0x06, 0x07, 0x08,
+        0x09, 0x0A, 0x0B, 0x0C,
+        0x0D, 0x0E, 0x0F, 0x00
+    };
+
+    /* USSD set message */
+    message = (mbim_message_ussd_set_new (
+                   MBIM_USSD_ACTION_CONTINUE,
+                   1, /* dcs */
+                   sizeof (payload),
+                   payload,
+                   &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_USSD);
+    g_assert_cmpuint (mbim_message_command_get_cid          (message), ==, MBIM_CID_USSD);
+    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);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_builder_auth_akap_query (void)
+{
+    GError *error = NULL;
+    MbimMessage *message;
+    const guint8 expected_message [] = {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x60, 0x00, 0x00, 0x00, /* length */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_message */
+        0x1D, 0x2B, 0x5F, 0xF7, /* service id */
+        0x0A, 0xA1, 0x48, 0xB2,
+        0xAA, 0x52, 0x50, 0xF1,
+        0x57, 0x67, 0x17, 0x4E,
+        0x02, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* command_type */
+        0x30, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x00, 0x01, 0x02, 0x03, /* 0x00 Rand */
+        0x04, 0x05, 0x06, 0x07, /* 0x04 */
+        0x08, 0x09, 0x0A, 0x0B, /* 0x08 */
+        0x0C, 0x0D, 0x0E, 0x0F, /* 0x0C */
+        0xFF, 0xFE, 0xFD, 0xFC, /* 0x10 Autn */
+        0xFB, 0xFA, 0xF9, 0xF8, /* 0x14 */
+        0xF7, 0xF6, 0xF5, 0xF4, /* 0x18 */
+        0xF3, 0xF2, 0xF1, 0xF0, /* 0x1C */
+        0x28, 0x00, 0x00, 0x00, /* 0x20 Network name (offset) */
+        0x08, 0x00, 0x00, 0x00, /* 0x24 Network name (length) */
+        /* data buffer */
+        0x31, 0x00, 0x31, 0x00, /* 0x28 Network name */
+        0x31, 0x00, 0x31, 0x00
+    };
+
+    const guint8 rand [] = {
+        0x00, 0x01, 0x02, 0x03,
+        0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B,
+        0x0C, 0x0D, 0x0E, 0x0F
+    };
+    const guint8 autn [] = {
+        0xFF, 0xFE, 0xFD, 0xFC,
+        0xFB, 0xFA, 0xF9, 0xF8,
+        0xF7, 0xF6, 0xF5, 0xF4,
+        0xF3, 0xF2, 0xF1, 0xF0
+    };
+
+    /* AKAP Auth message */
+    message = (mbim_message_auth_akap_query_new (
+                   rand,
+                   autn,
+                   "1111",
+                   &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_AUTH);
+    g_assert_cmpuint (mbim_message_command_get_cid          (message), ==, MBIM_CID_AUTH_AKAP);
+    g_assert_cmpuint (mbim_message_command_get_command_type (message), ==, MBIM_MESSAGE_COMMAND_TYPE_QUERY);
+
+    g_assert_cmpuint (((GByteArray *)message)->len, ==, sizeof (expected_message));
+    g_assert (memcmp (((GByteArray *)message)->data, expected_message, sizeof (expected_message)) == 0);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_builder_stk_pac_set (void)
+{
+    GError *error = NULL;
+    MbimMessage *message;
+    const guint8 expected_message [] = {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x50, 0x00, 0x00, 0x00, /* length */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_message */
+        0xD8, 0xF2, 0x01, 0x31, /* service id */
+        0xFC, 0xB5, 0x4E, 0x17,
+        0x86, 0x02, 0xD6, 0xED,
+        0x38, 0x16, 0x16, 0x4C,
+        0x01, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x20, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x00, 0x01, 0x02, 0x03,
+        0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B,
+        0x0C, 0x0D, 0x0E, 0x0F,
+        0xFF, 0xFE, 0xFD, 0xFC,
+        0xFB, 0xFA, 0xF9, 0xF8,
+        0xF7, 0xF6, 0xF5, 0xF4,
+        0xF3, 0xF2, 0xF1, 0xF0
+    };
+
+    const guint8 pac_host_control [] = {
+        0x00, 0x01, 0x02, 0x03,
+        0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B,
+        0x0C, 0x0D, 0x0E, 0x0F,
+        0xFF, 0xFE, 0xFD, 0xFC,
+        0xFB, 0xFA, 0xF9, 0xF8,
+        0xF7, 0xF6, 0xF5, 0xF4,
+        0xF3, 0xF2, 0xF1, 0xF0
+    };
+
+    /* STK PAC set message */
+    message = (mbim_message_stk_pac_set_new (
+                   pac_host_control,
+                   &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_STK);
+    g_assert_cmpuint (mbim_message_command_get_cid          (message), ==, MBIM_CID_STK_PAC);
+    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);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_builder_stk_terminal_response_set (void)
+{
+    GError *error = NULL;
+    MbimMessage *message;
+    const guint8 expected_message [] = {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x50, 0x00, 0x00, 0x00, /* length */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_message */
+        0xD8, 0xF2, 0x01, 0x31, /* service id */
+        0xFC, 0xB5, 0x4E, 0x17,
+        0x86, 0x02, 0xD6, 0xED,
+        0x38, 0x16, 0x16, 0x4C,
+        0x02, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x20, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x1C, 0x00, 0x00, 0x00, /* 0x00 Response length */
+        0x04, 0x05, 0x06, 0x07, /* 0x04 Response */
+        0x08, 0x09, 0x0A, 0x0B,
+        0x0C, 0x0D, 0x0E, 0x0F,
+        0xFF, 0xFE, 0xFD, 0xFC,
+        0xFB, 0xFA, 0xF9, 0xF8,
+        0xF7, 0xF6, 0xF5, 0xF4,
+        0xF3, 0xF2, 0xF1, 0xF0
+    };
+
+    const guint8 response [] = {
+        0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B,
+        0x0C, 0x0D, 0x0E, 0x0F,
+        0xFF, 0xFE, 0xFD, 0xFC,
+        0xFB, 0xFA, 0xF9, 0xF8,
+        0xF7, 0xF6, 0xF5, 0xF4,
+        0xF3, 0xF2, 0xF1, 0xF0
+    };
+
+    /* STK Terminal Response set message */
+    message = (mbim_message_stk_terminal_response_set_new (
+                   sizeof (response),
+                   response,
+                   &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_STK);
+    g_assert_cmpuint (mbim_message_command_get_cid          (message), ==, MBIM_CID_STK_TERMINAL_RESPONSE);
+    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);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_builder_stk_envelope_set (void)
+{
+    GError *error = NULL;
+    MbimMessage *message;
+    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_message */
+        0xD8, 0xF2, 0x01, 0x31, /* service id */
+        0xFC, 0xB5, 0x4E, 0x17,
+        0x86, 0x02, 0xD6, 0xED,
+        0x38, 0x16, 0x16, 0x4C,
+        0x03, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x28, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x1C, 0x00, 0x00, 0x00,
+        0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B,
+        0x0C, 0x0D, 0x0E, 0x0F,
+        0xFF, 0xFE, 0xFD, 0xFC,
+        0xFB, 0xFA, 0xF9, 0xF8,
+        0xF7, 0xF6, 0xF5, 0xF4,
+        0xF3, 0xF2, 0xF1, 0xF0,
+        0xFF, 0xFE, 0xFD, 0xFC,
+        0xFB, 0xFA, 0xF9, 0xF8
+    };
+
+    const guint8 databuffer [] = {
+        0x1C, 0x00, 0x00, 0x00,
+        0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B,
+        0x0C, 0x0D, 0x0E, 0x0F,
+        0xFF, 0xFE, 0xFD, 0xFC,
+        0xFB, 0xFA, 0xF9, 0xF8,
+        0xF7, 0xF6, 0xF5, 0xF4,
+        0xF3, 0xF2, 0xF1, 0xF0,
+        0xFF, 0xFE, 0xFD, 0xFC,
+        0xFB, 0xFA, 0xF9, 0xF8
+    };
+
+    /* STK Envelop set message */
+    message = (mbim_message_stk_envelope_set_new (
+                   sizeof (databuffer),
+                   databuffer,
+                   &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_STK);
+    g_assert_cmpuint (mbim_message_command_get_cid          (message), ==, MBIM_CID_STK_ENVELOPE);
+    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);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_builder_basic_connect_ip_packet_filters_set_none (void)
+{
+    GError *error = NULL;
+    MbimMessage *message;
+    const guint8 expected_message [] = {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x38, 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,
+        0x17, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x08, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x01, 0x00, 0x00, 0x00, /* 0x00 session id */
+        0x00, 0x00, 0x00, 0x00  /* 0x04 packet filters count */
+    };
+
+    /* IP packet filters set message */
+    message = mbim_message_ip_packet_filters_set_new (1, 0, NULL, &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_IP_PACKET_FILTERS);
+    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);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_builder_basic_connect_ip_packet_filters_set_one (void)
+{
+    GError *error = NULL;
+    MbimMessage *message;
+    MbimPacketFilter **filters;
+    const guint8 expected_message [] = {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x5C, 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,
+        0x17, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x2C, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x01, 0x00, 0x00, 0x00, /* 0x00 session id */
+        0x01, 0x00, 0x00, 0x00, /* 0x04 packet filters count */
+        0x10, 0x00, 0x00, 0x00, /* 0x08 packet filter 1 offset */
+        0x1C, 0x00, 0x00, 0x00, /* 0x0C packet filter 1 length */
+        /* databuffer, packet filter 1 */
+        0x07, 0x00, 0x00, 0x00, /* 0x10 0x00 filter size */
+        0x0C, 0x00, 0x00, 0x00, /* 0x14 0x04 filter offset (from beginning of struct) */
+        0x14, 0x00, 0x00, 0x00, /* 0x18 0x08 mask offset (from beginning of struct) */
+        0x01, 0x02, 0x03, 0x04, /* 0x1C 0x0C filter (padding 1) */
+        0x05, 0x06, 0x07, 0x00,
+        0xF1, 0xF2, 0xF3, 0xF4, /* 0x24 0x14 mask (padding 1) */
+        0xF5, 0xF6, 0xF7, 0x00,
+    };
+
+    const guint8 filter[] = {
+        0x01, 0x02, 0x03, 0x04,
+        0x05, 0x06, 0x07,
+    };
+    const guint8 mask[] = {
+        0xF1, 0xF2, 0xF3, 0xF4,
+        0xF5, 0xF6, 0xF7,
+    };
+
+
+    filters = g_new0 (MbimPacketFilter *, 2);
+    filters[0] = g_new (MbimPacketFilter, 1);
+    filters[0]->filter_size = 7;
+    filters[0]->packet_filter = g_new (guint8, 7);
+    memcpy (filters[0]->packet_filter, filter, 7);
+    filters[0]->packet_mask = g_new (guint8, 7);
+    memcpy (filters[0]->packet_mask, mask, 7);
+
+    /* IP packet filters set message */
+    message = (mbim_message_ip_packet_filters_set_new (
+                   1,
+                   1,
+                   (const MbimPacketFilter * const*)filters,
+                   &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_IP_PACKET_FILTERS);
+    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);
+
+    mbim_message_unref (message);
+
+    mbim_packet_filter_array_free (filters);
+}
+
+static void
+test_message_builder_basic_connect_ip_packet_filters_set_two (void)
+{
+    GError *error = NULL;
+    MbimMessage *message;
+    MbimPacketFilter **filters;
+    const guint8 expected_message [] = {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x88, 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,
+        0x17, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x58, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x01, 0x00, 0x00, 0x00, /* 0x00 session id */
+        0x02, 0x00, 0x00, 0x00, /* 0x04 packet filters count */
+        0x18, 0x00, 0x00, 0x00, /* 0x08 packet filter 1 offset */
+        0x1C, 0x00, 0x00, 0x00, /* 0x0C packet filter 1 length */
+        0x34, 0x00, 0x00, 0x00, /* 0x10 packet filter 2 offset */
+        0x24, 0x00, 0x00, 0x00, /* 0x14 packet filter 2 length */
+        /* databuffer, packet filter 2 */
+        0x08, 0x00, 0x00, 0x00, /* 0x18 0x00 filter size */
+        0x0C, 0x00, 0x00, 0x00, /* 0x1C 0x04 filter offset (from beginning of struct) */
+        0x14, 0x00, 0x00, 0x00, /* 0x20 0x08 mask offset (from beginning of struct) */
+        0x01, 0x02, 0x03, 0x04, /* 0x24 0x0C filter */
+        0x05, 0x06, 0x07, 0x08,
+        0xF1, 0xF2, 0xF3, 0xF4, /* 0x2C 0x14 mask */
+        0xF5, 0xF6, 0xF7, 0xF8,
+        /* databuffer, packet filter 2 */
+        0x0C, 0x00, 0x00, 0x00, /* 0x34 0x00 filter size */
+        0x0C, 0x00, 0x00, 0x00, /* 0x38 0x04 filter offset (from beginning of struct) */
+        0x18, 0x00, 0x00, 0x00, /* 0x3C 0x08 mask offset (from beginning of struct) */
+        0x01, 0x02, 0x03, 0x04, /* 0x40 0x0C filter */
+        0x05, 0x06, 0x07, 0x08,
+        0x05, 0x06, 0x07, 0x08,
+        0xF1, 0xF2, 0xF3, 0xF4, /* 0x4C 0x18 mask */
+        0xF5, 0xF6, 0xF7, 0xF8,
+        0xF5, 0xF6, 0xF7, 0xF8,
+    };
+
+    const guint8 filter1[] = {
+        0x01, 0x02, 0x03, 0x04,
+        0x05, 0x06, 0x07, 0x08,
+    };
+    const guint8 mask1[] = {
+        0xF1, 0xF2, 0xF3, 0xF4,
+        0xF5, 0xF6, 0xF7, 0xF8,
+    };
+    const guint8 filter2[] = {
+        0x01, 0x02, 0x03, 0x04,
+        0x05, 0x06, 0x07, 0x08,
+        0x05, 0x06, 0x07, 0x08,
+    };
+    const guint8 mask2[] = {
+        0xF1, 0xF2, 0xF3, 0xF4,
+        0xF5, 0xF6, 0xF7, 0xF8,
+        0xF5, 0xF6, 0xF7, 0xF8,
+    };
+
+    filters = g_new0 (MbimPacketFilter *, 3);
+    filters[0] = g_new (MbimPacketFilter, 1);
+    filters[0]->filter_size = 8;
+    filters[0]->packet_filter = g_new (guint8, 8);
+    memcpy (filters[0]->packet_filter, filter1, 8);
+    filters[0]->packet_mask = g_new (guint8, 8);
+    memcpy (filters[0]->packet_mask, mask1, 8);
+    filters[1] = g_new (MbimPacketFilter, 1);
+    filters[1]->filter_size = 12;
+    filters[1]->packet_filter = g_new (guint8, 12);
+    memcpy (filters[1]->packet_filter, filter2, 12);
+    filters[1]->packet_mask = g_new (guint8, 12);
+    memcpy (filters[1]->packet_mask, mask2, 12);
+
+    /* IP packet filters set message */
+    message = (mbim_message_ip_packet_filters_set_new (
+                   1,
+                   2,
+                   (const MbimPacketFilter * const*)filters,
+                   &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_IP_PACKET_FILTERS);
+    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);
+
+    mbim_message_unref (message);
+
+    mbim_packet_filter_array_free (filters);
+}
+
+static void
+test_message_builder_dss_connect_set (void)
+{
+    GError *error = NULL;
+    MbimMessage *message;
+    const guint8 expected_message [] = {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0x48, 0x00, 0x00, 0x00, /* length */
+        0x01, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_message */
+        0xC0, 0x8A, 0x26, 0xDD, /* service id */
+        0x77, 0x18, 0x43, 0x82,
+        0x84, 0x82, 0x6E, 0x0D,
+        0x58, 0x3C, 0x4D, 0x0E,
+        0x01, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x18, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x01, 0x02, 0x03, 0x04, /* service id */
+        0xFF, 0xFF, 0xBB, 0xBB,
+        0xFF, 0xCC, 0xF0, 0xF1,
+        0xF2, 0xF3, 0xF4, 0xF5,
+        0xFF, 0x00, 0x00, 0x00, /* dss session id */
+        0x01, 0x00, 0x00, 0x00  /* dss link state */
+    };
+
+    static const MbimUuid another_uuid = {
+        .a = { 0x01, 0x02, 0x03, 0x04 },
+        .b = { 0xFF, 0xFF },
+        .c = { 0xBB, 0xBB },
+        .d = { 0xFF, 0xCC },
+        .e = { 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5 }
+    };
+
+    /* IP packet filters set message */
+    message = (mbim_message_dss_connect_set_new (
+                   &another_uuid,
+                   255,
+                   MBIM_DSS_LINK_STATE_ACTIVATE,
+                   &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_DSS);
+    g_assert_cmpuint (mbim_message_command_get_cid          (message), ==, MBIM_CID_DSS_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);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_builder_basic_connect_multicarrier_providers_set (void)
+{
+    GError *error = NULL;
+    MbimMessage *message;
+    MbimProvider **providers;
+    const guint8 expected_message [] = {
+        /* header */
+        0x03, 0x00, 0x00, 0x00, /* type */
+        0xB4, 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,
+        0x18, 0x00, 0x00, 0x00, /* command id */
+        0x01, 0x00, 0x00, 0x00, /* command_type */
+        0x84, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x02, 0x00, 0x00, 0x00, /* 0x00 providers count */
+        0x14, 0x00, 0x00, 0x00, /* 0x04 provider 0 offset */
+        0x38, 0x00, 0x00, 0x00, /* 0x08 provider 0 length */
+        0x4C, 0x00, 0x00, 0x00, /* 0x0C provider 1 offset */
+        0x38, 0x00, 0x00, 0x00, /* 0x10 provider 1 length */
+        /* data buffer... struct provider 0 */
+        0x20, 0x00, 0x00, 0x00, /* 0x14 [0x00] id offset */
+        0x0A, 0x00, 0x00, 0x00, /* 0x18 [0x04] id length */
+        0x08, 0x00, 0x00, 0x00, /* 0x1C [0x08] state */
+        0x2C, 0x00, 0x00, 0x00, /* 0x20 [0x0C] name offset */
+        0x0C, 0x00, 0x00, 0x00, /* 0x24 [0x10] name length */
+        0x01, 0x00, 0x00, 0x00, /* 0x28 [0x14] cellular class */
+        0x0B, 0x00, 0x00, 0x00, /* 0x2C [0x18] rssi */
+        0x00, 0x00, 0x00, 0x00, /* 0x30 [0x1C] error rate */
+        0x32, 0x00, 0x31, 0x00, /* 0x34 [0x20] id string (10 bytes) */
+        0x34, 0x00, 0x30, 0x00,
+        0x33, 0x00, 0x00, 0x00,
+        0x4F, 0x00, 0x72, 0x00, /* 0x40 [0x2C] name string (12 bytes) */
+        0x61, 0x00, 0x6E, 0x00,
+        0x67, 0x00, 0x65, 0x00,
+        /* data buffer... struct provider 1 */
+        0x20, 0x00, 0x00, 0x00, /* 0x4C [0x00] id offset */
+        0x0A, 0x00, 0x00, 0x00, /* 0x50 [0x04] id length */
+        0x19, 0x00, 0x00, 0x00, /* 0x51 [0x08] state */
+        0x2C, 0x00, 0x00, 0x00, /* 0x54 [0x0C] name offset */
+        0x0C, 0x00, 0x00, 0x00, /* 0x58 [0x10] name length */
+        0x01, 0x00, 0x00, 0x00, /* 0x5C [0x14] cellular class */
+        0x0B, 0x00, 0x00, 0x00, /* 0x60 [0x18] rssi */
+        0x00, 0x00, 0x00, 0x00, /* 0x64 [0x1C] error rate */
+        0x32, 0x00, 0x31, 0x00, /* 0x68 [0x20] id string (10 bytes) */
+        0x34, 0x00, 0x30, 0x00,
+        0x33, 0x00, 0x00, 0x00,
+        0x4F, 0x00, 0x72, 0x00, /* 0x74 [0x2C] name string (12 bytes) */
+        0x61, 0x00, 0x6E, 0x00,
+        0x67, 0x00, 0x65, 0x00
+    };
+
+    providers = g_new0 (MbimProvider *, 3);
+    providers[0] = g_new0 (MbimProvider, 1);
+    providers[0]->provider_id = g_strdup ("21403");
+    providers[0]->provider_name = g_strdup ("Orange");
+    providers[0]->provider_state = MBIM_PROVIDER_STATE_VISIBLE;
+    providers[0]->cellular_class = MBIM_CELLULAR_CLASS_GSM;
+    providers[0]->rssi = 11;
+    providers[0]->error_rate = 0;
+    providers[1] = g_new0 (MbimProvider, 1);
+    providers[1]->provider_id = g_strdup ("21403");
+    providers[1]->provider_name = g_strdup ("Orange");
+    providers[1]->provider_state = (MBIM_PROVIDER_STATE_HOME |
+                                    MBIM_PROVIDER_STATE_VISIBLE |
+                                    MBIM_PROVIDER_STATE_REGISTERED);
+    providers[1]->cellular_class = MBIM_CELLULAR_CLASS_GSM;
+    providers[1]->rssi = 11;
+    providers[1]->error_rate = 0;
+
+    /* Multicarrier providers set message */
+    message = (mbim_message_multicarrier_providers_set_new (
+                   2,
+                   (const MbimProvider *const *)providers,
+                   &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_MULTICARRIER_PROVIDERS);
+    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);
+
+    mbim_message_unref (message);
+    mbim_provider_array_free (providers);
+}
+
+int main (int argc, char **argv)
+{
+    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-subscriber-list/set", test_message_builder_basic_connect_device_service_subscriber_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);
+
+    return g_test_run ();
+}
diff --git a/libmbim-glib/test/test-message-parser.c b/libmbim-glib/test/test-message-parser.c
new file mode 100644
index 0000000..1f33805
--- /dev/null
+++ b/libmbim-glib/test/test-message-parser.c
@@ -0,0 +1,1587 @@
+/* -*- Mode: C; 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 the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "mbim-basic-connect.h"
+#include "mbim-sms.h"
+#include "mbim-ussd.h"
+#include "mbim-auth.h"
+#include "mbim-stk.h"
+#include "mbim-message.h"
+#include "mbim-cid.h"
+#include "mbim-utils.h"
+
+#if defined ENABLE_TEST_MESSAGE_TRACES
+static void
+test_message_trace (const guint8 *computed,
+                    guint32       computed_size,
+                    const guint8 *expected,
+                    guint32       expected_size)
+{
+    gchar *message_str;
+    gchar *expected_str;
+
+    message_str = __mbim_utils_str_hex (computed, computed_size, ':');
+    expected_str = __mbim_utils_str_hex (expected, expected_size, ':');
+
+    /* Dump all message contents */
+    g_print ("\n"
+             "Message str:\n"
+             "'%s'\n"
+             "Expected str:\n"
+             "'%s'\n",
+             message_str,
+             expected_str);
+
+    /* If they are different, tell which are the different bytes */
+    if (computed_size != expected_size ||
+        memcmp (computed, expected, expected_size)) {
+        guint32 i;
+
+        for (i = 0; i < MIN (computed_size, expected_size); i++) {
+            if (computed[i] != expected[i])
+                g_print ("Byte [%u] is different (computed: 0x%02X vs expected: 0x%02x)\n", i, computed[i], expected[i]);
+        }
+    }
+
+    g_free (message_str);
+    g_free (expected_str);
+}
+#else
+#define test_message_trace(...)
+#endif
+
+static void
+test_message_parser_basic_connect_visible_providers (void)
+{
+    MbimProvider **providers;
+    guint32 n_providers;
+    MbimMessage *response;
+    GError *error = NULL;
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0xB4, 0x00, 0x00, 0x00, /* length */
+        0x02, 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,
+        0x08, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x84, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x02, 0x00, 0x00, 0x00, /* 0x00 providers count */
+        0x14, 0x00, 0x00, 0x00, /* 0x04 provider 0 offset */
+        0x38, 0x00, 0x00, 0x00, /* 0x08 provider 0 length */
+        0x4C, 0x00, 0x00, 0x00, /* 0x0C provider 1 offset */
+        0x38, 0x00, 0x00, 0x00, /* 0x10 provider 1 length */
+        /* data buffer... struct provider 0 */
+        0x20, 0x00, 0x00, 0x00, /* 0x14 [0x00] id offset */
+        0x0A, 0x00, 0x00, 0x00, /* 0x18 [0x04] id length */
+        0x08, 0x00, 0x00, 0x00, /* 0x1C [0x08] state */
+        0x2C, 0x00, 0x00, 0x00, /* 0x20 [0x0C] name offset */
+        0x0C, 0x00, 0x00, 0x00, /* 0x24 [0x10] name length */
+        0x01, 0x00, 0x00, 0x00, /* 0x28 [0x14] cellular class */
+        0x0B, 0x00, 0x00, 0x00, /* 0x2C [0x18] rssi */
+        0x00, 0x00, 0x00, 0x00, /* 0x30 [0x1C] error rate */
+        0x32, 0x00, 0x31, 0x00, /* 0x34 [0x20] id string (10 bytes) */
+        0x34, 0x00, 0x30, 0x00,
+        0x33, 0x00, 0x00, 0x00,
+        0x4F, 0x00, 0x72, 0x00, /* 0x40 [0x2C] name string (12 bytes) */
+        0x61, 0x00, 0x6E, 0x00,
+        0x67, 0x00, 0x65, 0x00,
+        /* data buffer... struct provider 1 */
+        0x20, 0x00, 0x00, 0x00, /* 0x4C [0x00] id offset */
+        0x0A, 0x00, 0x00, 0x00, /* 0x50 [0x04] id length */
+        0x19, 0x00, 0x00, 0x00, /* 0x51 [0x08] state */
+        0x2C, 0x00, 0x00, 0x00, /* 0x54 [0x0C] name offset */
+        0x0C, 0x00, 0x00, 0x00, /* 0x58 [0x10] name length */
+        0x01, 0x00, 0x00, 0x00, /* 0x5C [0x14] cellular class */
+        0x0B, 0x00, 0x00, 0x00, /* 0x60 [0x18] rssi */
+        0x00, 0x00, 0x00, 0x00, /* 0x64 [0x1C] error rate */
+        0x32, 0x00, 0x31, 0x00, /* 0x68 [0x20] id string (10 bytes) */
+        0x34, 0x00, 0x30, 0x00,
+        0x33, 0x00, 0x00, 0x00,
+        0x4F, 0x00, 0x72, 0x00, /* 0x74 [0x2C] name string (12 bytes) */
+        0x61, 0x00, 0x6E, 0x00,
+        0x67, 0x00, 0x65, 0x00 };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_visible_providers_response_parse (
+                  response,
+                  &n_providers,
+                  &providers,
+                  &error));
+
+    g_assert_no_error (error);
+
+    g_assert_cmpuint (n_providers, ==, 2);
+
+    /* Provider [0]
+     * Provider ID: '21403'
+     * Provider Name: 'Orange'
+     * State: 'visible'
+     * Cellular class: 'gsm'
+     * RSSI: '11'
+     * Error rate: '0'
+     */
+    g_assert_cmpstr (providers[0]->provider_id, ==, "21403");
+    g_assert_cmpstr (providers[0]->provider_name, ==, "Orange");
+    g_assert_cmpuint (providers[0]->provider_state, ==, MBIM_PROVIDER_STATE_VISIBLE);
+    g_assert_cmpuint (providers[0]->cellular_class, ==, MBIM_CELLULAR_CLASS_GSM);
+    g_assert_cmpuint (providers[0]->rssi, ==, 11);
+    g_assert_cmpuint (providers[0]->error_rate, ==, 0);
+
+	/* Provider [1]:
+     * Provider ID: '21403'
+     * Provider Name: 'Orange'
+     * State: 'home, visible, registered'
+     * Cellular class: 'gsm'
+     * RSSI: '11'
+     * Error rate: '0'
+     */
+    g_assert_cmpstr (providers[1]->provider_id, ==, "21403");
+    g_assert_cmpstr (providers[1]->provider_name, ==, "Orange");
+    g_assert_cmpuint (providers[1]->provider_state, ==, (MBIM_PROVIDER_STATE_HOME |
+                                                         MBIM_PROVIDER_STATE_VISIBLE |
+                                                         MBIM_PROVIDER_STATE_REGISTERED));
+    g_assert_cmpuint (providers[1]->cellular_class, ==, MBIM_CELLULAR_CLASS_GSM);
+    g_assert_cmpuint (providers[1]->rssi, ==, 11);
+    g_assert_cmpuint (providers[1]->error_rate, ==, 0);
+
+    mbim_provider_array_free (providers);
+    mbim_message_unref (response);
+}
+
+static void
+test_message_parser_basic_connect_subscriber_ready_status (void)
+{
+    MbimSubscriberReadyState ready_state;
+    gchar *subscriber_id;
+    gchar *sim_iccid;
+    MbimReadyInfoFlag ready_info;
+    guint32 telephone_numbers_count;
+    gchar **telephone_numbers;
+    MbimMessage *response;
+    GError *error = NULL;
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0xB4, 0x00, 0x00, 0x00, /* length */
+        0x02, 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,
+        0x02, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x84, 0x00, 0x00, 0x00, /* buffer_length */
+        /* information buffer */
+        0x01, 0x00, 0x00, 0x00, /* 0x00 ready state */
+        0x5C, 0x00, 0x00, 0x00, /* 0x04 subscriber id (offset) */
+        0x1E, 0x00, 0x00, 0x00, /* 0x08 subscriber id (size) */
+        0x7C, 0x00, 0x00, 0x00, /* 0x0C sim iccid (offset) */
+        0x28, 0x00, 0x00, 0x00, /* 0x10 sim iccid (size) */
+        0x00, 0x00, 0x00, 0x00, /* 0x14 ready info */
+        0x02, 0x00, 0x00, 0x00, /* 0x18 telephone numbers count */
+        0x2C, 0x00, 0x00, 0x00, /* 0x1C telephone number #1 (offset) */
+        0x16, 0x00, 0x00, 0x00, /* 0x20 telephone number #1 (size) */
+        0x44, 0x00, 0x00, 0x00, /* 0x24 telephone number #2 (offset) */
+        0x16, 0x00, 0x00, 0x00, /* 0x28 telephone number #2 (size) */
+        /* data buffer */
+        0x31, 0x00, 0x31, 0x00, /* 0x2C telephone number #1 (data) */
+        0x31, 0x00, 0x31, 0x00,
+        0x31, 0x00, 0x31, 0x00,
+        0x31, 0x00, 0x31, 0x00,
+        0x31, 0x00, 0x31, 0x00,
+        0x31, 0x00, 0x00, 0x00, /* last 2 bytes are padding */
+        0x30, 0x00, 0x30, 0x00, /* 0x44 telephone number #2 (data) */
+        0x30, 0x00, 0x30, 0x00,
+        0x30, 0x00, 0x30, 0x00,
+        0x30, 0x00, 0x30, 0x00,
+        0x30, 0x00, 0x30, 0x00,
+        0x30, 0x00, 0x00, 0x00, /* last 2 bytes are padding */
+        0x33, 0x00, 0x31, 0x00, /* 0x5C subscriber id (data) */
+        0x30, 0x00, 0x34, 0x00,
+        0x31, 0x00, 0x30, 0x00,
+        0x30, 0x00, 0x30, 0x00,
+        0x30, 0x00, 0x31, 0x00,
+        0x31, 0x00, 0x30, 0x00,
+        0x37, 0x00, 0x36, 0x00,
+        0x31, 0x00, 0x00, 0x00, /* last 2 bytes are padding */
+        0x38, 0x00, 0x39, 0x00, /* 0x7C sim iccid (data) */
+        0x30, 0x00, 0x31, 0x00,
+        0x30, 0x00, 0x31, 0x00,
+        0x30, 0x00, 0x34, 0x00,
+        0x30, 0x00, 0x35, 0x00,
+        0x34, 0x00, 0x36, 0x00,
+        0x30, 0x00, 0x31, 0x00,
+        0x31, 0x00, 0x30, 0x00,
+        0x30, 0x00, 0x36, 0x00,
+        0x31, 0x00, 0x32, 0x00 };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_subscriber_ready_status_response_parse (
+                  response,
+                  &ready_state,
+                  &subscriber_id,
+                  &sim_iccid,
+                  &ready_info,
+                  &telephone_numbers_count,
+                  &telephone_numbers,
+                  &error));
+
+    g_assert_no_error (error);
+
+    g_assert_cmpuint (ready_state, ==, MBIM_SUBSCRIBER_READY_STATE_INITIALIZED);
+    g_assert_cmpstr (subscriber_id, ==, "310410000110761");
+    g_assert_cmpstr (sim_iccid, ==, "89010104054601100612");
+    g_assert_cmpuint (ready_info, ==, 0);
+    g_assert_cmpuint (telephone_numbers_count, ==, 2);
+    g_assert_cmpstr (telephone_numbers[0], ==, "11111111111");
+    g_assert_cmpstr (telephone_numbers[1], ==, "00000000000");
+    g_assert (telephone_numbers[2] == NULL);
+
+    g_free (subscriber_id);
+    g_free (sim_iccid);
+    g_strfreev (telephone_numbers);
+
+    mbim_message_unref (response);
+}
+
+static void
+test_message_parser_basic_connect_device_caps (void)
+{
+    MbimMessage *response;
+    MbimDeviceType device_type;
+    MbimCellularClass cellular_class;
+    MbimVoiceClass voice_class;
+    MbimSimClass sim_class;
+    MbimDataClass data_class;
+    MbimSmsCaps sms_caps;
+    MbimCtrlCaps ctrl_caps;
+    guint32 max_sessions;
+    gchar *custom_data_class;
+    gchar *device_id;
+    gchar *firmware_info;
+    gchar *hardware_info;
+    GError *error = NULL;
+    const guint8 buffer [] =  { 0x03, 0x00, 0x00, 0x80,
+                                0xD0, 0x00, 0x00, 0x00,
+                                0x02, 0x00, 0x00, 0x00,
+                                0x01, 0x00, 0x00, 0x00,
+                                0x00, 0x00, 0x00, 0x00,
+                                0xA2, 0x89, 0xCC, 0x33,
+                                0xBC, 0xBB, 0x8B, 0x4F,
+                                0xB6, 0xB0, 0x13, 0x3E,
+                                0xC2, 0xAA, 0xE6, 0xDF,
+                                0x01, 0x00, 0x00, 0x00,
+                                0x00, 0x00, 0x00, 0x00,
+                                0xA0, 0x00, 0x00, 0x00,
+                                0x02, 0x00, 0x00, 0x00,
+                                0x01, 0x00, 0x00, 0x00,
+                                0x01, 0x00, 0x00, 0x00,
+                                0x02, 0x00, 0x00, 0x00,
+                                0x1F, 0x00, 0x00, 0x80,
+                                0x03, 0x00, 0x00, 0x00,
+                                0x01, 0x00, 0x00, 0x00,
+                                0x01, 0x00, 0x00, 0x00,
+                                0x40, 0x00, 0x00, 0x00,
+                                0x0A, 0x00, 0x00, 0x00,
+                                0x4C, 0x00, 0x00, 0x00,
+                                0x1E, 0x00, 0x00, 0x00,
+                                0x6C, 0x00, 0x00, 0x00,
+                                0x1E, 0x00, 0x00, 0x00,
+                                0x8C, 0x00, 0x00, 0x00,
+                                0x12, 0x00, 0x00, 0x00,
+                                0x48, 0x00, 0x53, 0x00,
+                                0x50, 0x00, 0x41, 0x00,
+                                0x2B, 0x00, 0x00, 0x00,
+                                0x33, 0x00, 0x35, 0x00,
+                                0x33, 0x00, 0x36, 0x00,
+                                0x31, 0x00, 0x33, 0x00,
+                                0x30, 0x00, 0x34, 0x00,
+                                0x38, 0x00, 0x38, 0x00,
+                                0x30, 0x00, 0x34, 0x00,
+                                0x36, 0x00, 0x32, 0x00,
+                                0x32, 0x00, 0x00, 0x00,
+                                0x31, 0x00, 0x31, 0x00,
+                                0x2E, 0x00, 0x38, 0x00,
+                                0x31, 0x00, 0x30, 0x00,
+                                0x2E, 0x00, 0x30, 0x00,
+                                0x39, 0x00, 0x2E, 0x00,
+                                0x30, 0x00, 0x30, 0x00,
+                                0x2E, 0x00, 0x30, 0x00,
+                                0x30, 0x00, 0x00, 0x00,
+                                0x43, 0x00, 0x50, 0x00,
+                                0x31, 0x00, 0x45, 0x00,
+                                0x33, 0x00, 0x36, 0x00,
+                                0x37, 0x00, 0x55, 0x00,
+                                0x4D, 0x00, 0x00, 0x00 };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_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,
+                  &error));
+
+    g_assert_no_error (error);
+
+    g_assert_cmpuint (device_type, ==, MBIM_DEVICE_TYPE_REMOVABLE);
+    g_assert_cmpuint (cellular_class, ==, MBIM_CELLULAR_CLASS_GSM);
+    g_assert_cmpuint (sim_class, ==, MBIM_SIM_CLASS_REMOVABLE);
+    g_assert_cmpuint (data_class, ==, (MBIM_DATA_CLASS_GPRS |
+                                       MBIM_DATA_CLASS_EDGE |
+                                       MBIM_DATA_CLASS_UMTS |
+                                       MBIM_DATA_CLASS_HSDPA |
+                                       MBIM_DATA_CLASS_HSUPA |
+                                       MBIM_DATA_CLASS_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);
+    g_assert_cmpuint (max_sessions, ==, 1);
+    g_assert_cmpstr (custom_data_class, ==, "HSPA+");
+    g_assert_cmpstr (device_id, ==, "353613048804622");
+    g_assert_cmpstr (firmware_info, ==, "11.810.09.00.00");
+    g_assert_cmpstr (hardware_info, ==, "CP1E367UM");
+
+    g_free (custom_data_class);
+    g_free (device_id);
+    g_free (firmware_info);
+    g_free (hardware_info);
+
+    mbim_message_unref (response);
+}
+
+static void
+test_message_parser_basic_connect_ip_configuration (void)
+{
+    MbimMessage *response;
+    guint32 session_id;
+    MbimIPConfigurationAvailableFlag ipv4configurationavailable;
+    MbimIPConfigurationAvailableFlag ipv6configurationavailable;
+    guint32 ipv4addresscount;
+    MbimIPv4Element **ipv4address;
+    guint32 ipv6addresscount;
+    MbimIPv6Element **ipv6address;
+    const MbimIPv4 *ipv4gateway;
+    const MbimIPv6 *ipv6gateway;
+    guint32 ipv4dnsservercount;
+    MbimIPv4 *ipv4dnsserver;
+    guint32 ipv6dnsservercount;
+    MbimIPv6 *ipv6dnsserver;
+    guint32 ipv4mtu;
+    guint32 ipv6mtu;
+    GError *error = NULL;
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x80, 0x00, 0x00, 0x00, /* length */
+        0x1A, 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,
+        0x0F, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x50, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x00, 0x00, 0x00, 0x00, /* session id */
+        0x0F, 0x00, 0x00, 0x00, /* IPv4ConfigurationAvailable */
+        0x00, 0x00, 0x00, 0x00, /* IPv6ConfigurationAvailable */
+        0x01, 0x00, 0x00, 0x00, /* IPv4 element count */
+        0x3C, 0x00, 0x00, 0x00, /* IPv4 element offset */
+        0x00, 0x00, 0x00, 0x00, /* IPv6 element count */
+        0x00, 0x00, 0x00, 0x00, /* IPv6 element offset */
+        0x44, 0x00, 0x00, 0x00, /* IPv4 gateway offset */
+        0x00, 0x00, 0x00, 0x00, /* IPv6 gateway offset */
+        0x02, 0x00, 0x00, 0x00, /* IPv4 DNS count */
+        0x48, 0x00, 0x00, 0x00, /* IPv4 DNS offset */
+        0x00, 0x00, 0x00, 0x00, /* IPv6 DNS count */
+        0x00, 0x00, 0x00, 0x00, /* IPv6 DNS offset */
+        0xDC, 0x05, 0x00, 0x00, /* IPv4 MTU */
+        0x00, 0x00, 0x00, 0x00, /* IPv6 MTU */
+        /* data buffer */
+        0x1C, 0x00, 0x00, 0x00, /* IPv4 element (netmask) */
+        0xD4, 0x49, 0x22, 0xF8, /* IPv4 element (address) */
+        0xD4, 0x49, 0x22, 0xF1, /* IPv4 gateway */
+        0xD4, 0xA6, 0xD2, 0x50, /* IPv4 DNS1 */
+        0xD4, 0x49, 0x20, 0x43  /* IPv4 DNS2 */
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_ip_configuration_response_parse (
+                  response,
+                  &session_id,
+                  &ipv4configurationavailable,
+                  &ipv6configurationavailable,
+                  &ipv4addresscount,
+                  &ipv4address,
+                  &ipv6addresscount,
+                  &ipv6address,
+                  &ipv4gateway,
+                  &ipv6gateway,
+                  &ipv4dnsservercount,
+                  &ipv4dnsserver,
+                  &ipv6dnsservercount,
+                  &ipv6dnsserver,
+                  &ipv4mtu,
+                  &ipv6mtu,
+                  &error));
+
+    /*
+     *   IPv4 configuration available: 'address, gateway, dns, mtu'
+     *     IP addresses (1)
+     *       IP [0]: '212.166.228.25/28'
+     *     Gateway: '212.166.228.26'
+     *     DNS addresses (2)
+     *       DNS [0]: '212.166.210.80'
+     *       DNS [1]: '212.73.32.67'
+     *     MTU: '1500'
+     */
+
+    g_assert_cmpuint (session_id, ==, 0);
+    g_assert_cmpuint (ipv4configurationavailable, ==, (MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS |
+                                                       MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY |
+                                                       MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS |
+                                                       MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU));
+    g_assert_cmpuint (ipv6configurationavailable, ==, MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_NONE);
+
+    {
+        MbimIPv4 addr = { .addr = { 0xD4, 0x49, 0x22, 0xF8 } };
+
+        g_assert_cmpuint (ipv4addresscount, ==, 1);
+        g_assert_cmpuint (ipv4address[0]->on_link_prefix_length, ==, 28);
+        g_assert (memcmp (&addr, &(ipv4address[0]->ipv4_address), 4) == 0);
+    }
+
+    {
+        MbimIPv4 gateway_addr = { .addr = { 0xD4, 0x49, 0x22, 0xF1 } };
+
+        g_assert (memcmp (&gateway_addr, ipv4gateway, 4) == 0);
+    }
+
+    {
+        MbimIPv4 dns_addr_1 = { .addr = { 0xD4, 0xA6, 0xD2, 0x50 } };
+        MbimIPv4 dns_addr_2 = { .addr = { 0xD4, 0x49, 0x20, 0x43 } };
+
+        g_assert_cmpuint (ipv4dnsservercount, ==, 2);
+        g_assert (memcmp (&dns_addr_1, &ipv4dnsserver[0], 4) == 0);
+        g_assert (memcmp (&dns_addr_2, &ipv4dnsserver[1], 4) == 0);
+    }
+
+    g_assert_cmpuint (ipv4mtu, ==, 1500);
+
+    g_assert_cmpuint (ipv6addresscount, ==, 0);
+    g_assert (ipv6address == NULL);
+    g_assert (ipv6gateway == NULL);
+    g_assert_cmpuint (ipv6dnsservercount, ==, 0);
+    g_assert (ipv6dnsserver == NULL);
+
+    mbim_ipv4_element_array_free (ipv4address);
+    mbim_ipv6_element_array_free (ipv6address);
+    g_free (ipv4dnsserver);
+    g_free (ipv6dnsserver);
+
+    mbim_message_unref (response);
+}
+
+static void
+test_message_parser_basic_connect_service_activation (void)
+{
+    MbimMessage *response;
+    GError *error = NULL;
+    guint32 nw_error;
+    const guint8 *databuffer;
+    guint32 databuffer_size;
+    const guint8 expected_databuffer [] =  {
+        0x01, 0x02, 0x03, 0x04,
+        0x05, 0x06, 0x07, 0x08
+    };
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x3C, 0x00, 0x00, 0x00, /* length */
+        0x02, 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,
+        0x0E, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x0C, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x06, 0x00, 0x00, 0x00, /* nw error */
+        0x01, 0x02, 0x03, 0x04, /* buffer */
+        0x05, 0x06, 0x07, 0x08  };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_service_activation_response_parse (
+                  response,
+                  &nw_error,
+                  &databuffer_size,
+                  &databuffer,
+                  &error));
+
+    g_assert_no_error (error);
+
+    g_assert_cmpuint (nw_error, ==, MBIM_NW_ERROR_ILLEGAL_ME);
+    g_assert_cmpuint (databuffer_size, ==, sizeof (expected_databuffer));
+    g_assert (memcmp (databuffer, expected_databuffer, databuffer_size) == 0);
+
+    mbim_message_unref (response);
+}
+
+static void
+test_message_parser_sms_read_zero_pdu (void)
+{
+    MbimSmsFormat format;
+    guint32 messages_count;
+    MbimSmsPduReadRecord **pdu_messages;
+    MbimSmsCdmaReadRecord **cdma_messages;
+    MbimMessage *response;
+    GError *error = NULL;
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0xB0, 0x00, 0x00, 0x00, /* length */
+        0x02, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0x53, 0x3F, 0xBE, 0xEB, /* service id */
+        0x14, 0xFE, 0x44, 0x67,
+        0x9F, 0x90, 0x33, 0xA2,
+        0x23, 0xE5, 0x6C, 0x3F,
+        0x02, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x38, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x00, 0x00, 0x00, 0x00, /* 0x00 format */
+        0x00, 0x00, 0x00, 0x00, /* 0x04 messages count */
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_sms_read_response_parse (
+                  response,
+                  &format,
+                  &messages_count,
+                  &pdu_messages,
+                  &cdma_messages,
+                  &error));
+    g_assert_no_error (error);
+
+    g_assert_cmpuint (format, ==, MBIM_SMS_FORMAT_PDU);
+    g_assert_cmpuint (messages_count, ==, 0);
+    g_assert (pdu_messages == NULL);
+    g_assert (cdma_messages == NULL);
+
+    mbim_message_unref (response);
+}
+
+static void
+test_message_parser_sms_read_single_pdu (void)
+{
+    MbimSmsFormat format;
+    guint32 messages_count;
+    MbimSmsPduReadRecord **pdu_messages;
+    MbimSmsCdmaReadRecord **cdma_messages;
+    MbimMessage *response;
+    GError *error = NULL;
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0xB0, 0x00, 0x00, 0x00, /* length */
+        0x02, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0x53, 0x3F, 0xBE, 0xEB, /* service id */
+        0x14, 0xFE, 0x44, 0x67,
+        0x9F, 0x90, 0x33, 0xA2,
+        0x23, 0xE5, 0x6C, 0x3F,
+        0x02, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x60, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x00, 0x00, 0x00, 0x00, /* 0x00 format */
+        0x01, 0x00, 0x00, 0x00, /* 0x04 messages count */
+        0x10, 0x00, 0x00, 0x00, /* 0x08 message 1 offset */
+        0x20, 0x00, 0x00, 0x00, /* 0x0C message 1 length */
+        /* data buffer... message 1 */
+        0x07, 0x00, 0x00, 0x00, /* 0x10 0x00 message index */
+        0x03, 0x00, 0x00, 0x00, /* 0x14 0x04 message status */
+        0x10, 0x00, 0x00, 0x00, /* 0x18 0x08 pdu data offset (w.r.t. pdu start */
+        0x10, 0x00, 0x00, 0x00, /* 0x1C 0x0C pdu data length */
+        /*    pdu data... */
+        0x01, 0x02, 0x03, 0x04, /* 0x20 0x10 */
+        0x05, 0x06, 0x07, 0x08,
+        0x09, 0x0A, 0x0B, 0x0C,
+        0x0D, 0x0E, 0x0F, 0x00
+    };
+
+    const guint8 expected_pdu [] = {
+        0x01, 0x02, 0x03, 0x04,
+        0x05, 0x06, 0x07, 0x08,
+        0x09, 0x0A, 0x0B, 0x0C,
+        0x0D, 0x0E, 0x0F, 0x00
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_sms_read_response_parse (
+                  response,
+                  &format,
+                  &messages_count,
+                  &pdu_messages,
+                  &cdma_messages,
+                  &error));
+    g_assert_no_error (error);
+
+    g_assert_cmpuint (format, ==, MBIM_SMS_FORMAT_PDU);
+    g_assert_cmpuint (messages_count, ==, 1);
+    g_assert (pdu_messages != NULL);
+    g_assert (cdma_messages == NULL);
+
+    g_assert_cmpuint (pdu_messages[0]->message_index, ==, 7);
+    g_assert_cmpuint (pdu_messages[0]->message_status, ==, MBIM_SMS_STATUS_SENT);
+    test_message_trace (pdu_messages[0]->pdu_data,
+                        pdu_messages[0]->pdu_data_size,
+                        expected_pdu,
+                        sizeof (expected_pdu));
+    g_assert_cmpuint (pdu_messages[0]->pdu_data_size, ==, sizeof (expected_pdu));
+    g_assert (memcmp (pdu_messages[0]->pdu_data, expected_pdu, sizeof (expected_pdu)) == 0);
+
+    mbim_sms_pdu_read_record_array_free (pdu_messages);
+    mbim_message_unref (response);
+}
+
+static void
+test_message_parser_sms_read_multiple_pdu (void)
+{
+    guint32 idx;
+    MbimSmsFormat format;
+    guint32 messages_count;
+    MbimSmsPduReadRecord **pdu_messages;
+    MbimSmsCdmaReadRecord **cdma_messages;
+    MbimMessage *response;
+    GError *error = NULL;
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0xB0, 0x00, 0x00, 0x00, /* length */
+        0x02, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0x53, 0x3F, 0xBE, 0xEB, /* service id */
+        0x14, 0xFE, 0x44, 0x67,
+        0x9F, 0x90, 0x33, 0xA2,
+        0x23, 0xE5, 0x6C, 0x3F,
+        0x02, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x60, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x00, 0x00, 0x00, 0x00, /* 0x00 format */
+        0x02, 0x00, 0x00, 0x00, /* 0x04 messages count */
+        0x18, 0x00, 0x00, 0x00, /* 0x08 message 1 offset */
+        0x20, 0x00, 0x00, 0x00, /* 0x0C message 1 length */
+        0x38, 0x00, 0x00, 0x00, /* 0x10 message 2 offset */
+        0x24, 0x00, 0x00, 0x00, /* 0x14 message 2 length */
+        /* data buffer... message 1 */
+        0x06, 0x00, 0x00, 0x00, /* 0x18 0x00 message index */
+        0x03, 0x00, 0x00, 0x00, /* 0x1C 0x04 message status */
+        0x10, 0x00, 0x00, 0x00, /* 0x20 0x08 pdu data offset (w.r.t. pdu start */
+        0x10, 0x00, 0x00, 0x00, /* 0x24 0x0C pdu data length */
+        /*    pdu data... */
+        0x01, 0x02, 0x03, 0x04, /* 0x28 0x10 */
+        0x05, 0x06, 0x07, 0x08,
+        0x09, 0x0A, 0x0B, 0x0C,
+        0x0D, 0x0E, 0x0F, 0x00,
+        /* data buffer... message 2 */
+        0x07, 0x00, 0x00, 0x00, /* 0x38 0x00 message index */
+        0x03, 0x00, 0x00, 0x00, /* 0x3C 0x04 message status */
+        0x10, 0x00, 0x00, 0x00, /* 0x40 0x08 pdu data offset (w.r.t. pdu start */
+        0x10, 0x00, 0x00, 0x00, /* 0x44 0x0C pdu data length */
+        /*    pdu data... */
+        0x00, 0x01, 0x02, 0x03, /* 0x48 0x10 */
+        0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B,
+        0x0C, 0x0D, 0x0E, 0x0F
+    };
+
+    const guint8 expected_pdu_idx6 [] = {
+        0x01, 0x02, 0x03, 0x04,
+        0x05, 0x06, 0x07, 0x08,
+        0x09, 0x0A, 0x0B, 0x0C,
+        0x0D, 0x0E, 0x0F, 0x00
+    };
+
+    const guint8 expected_pdu_idx7 [] = {
+        0x00, 0x01, 0x02, 0x03,
+        0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B,
+        0x0C, 0x0D, 0x0E, 0x0F
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_sms_read_response_parse (
+                  response,
+                  &format,
+                  &messages_count,
+                  &pdu_messages,
+                  &cdma_messages,
+                  &error));
+    g_assert_no_error (error);
+
+    g_assert_cmpuint (format, ==, MBIM_SMS_FORMAT_PDU);
+    g_assert_cmpuint (messages_count, ==, 2);
+    g_assert (pdu_messages != NULL);
+    g_assert (cdma_messages == NULL);
+
+    /* First message with index 6 */
+    if (pdu_messages[0]->message_index == 6)
+        idx = 0;
+    else if (pdu_messages[1]->message_index == 6)
+        idx = 1;
+    else
+        g_assert_not_reached ();
+    g_assert_cmpuint (pdu_messages[idx]->message_index, ==, 6);
+    g_assert_cmpuint (pdu_messages[idx]->message_status, ==, MBIM_SMS_STATUS_SENT);
+    test_message_trace (pdu_messages[idx]->pdu_data,
+                        pdu_messages[idx]->pdu_data_size,
+                        expected_pdu_idx6,
+                        sizeof (expected_pdu_idx6));
+    g_assert_cmpuint (pdu_messages[idx]->pdu_data_size, ==, sizeof (expected_pdu_idx6));
+    g_assert (memcmp (pdu_messages[idx]->pdu_data, expected_pdu_idx6, sizeof (expected_pdu_idx6)) == 0);
+
+    /* Second message with index 7 */
+    if (pdu_messages[0]->message_index == 7)
+        idx = 0;
+    else if (pdu_messages[1]->message_index == 7)
+        idx = 1;
+    else
+        g_assert_not_reached ();
+    g_assert_cmpuint (pdu_messages[idx]->message_index, ==, 7);
+    g_assert_cmpuint (pdu_messages[idx]->message_status, ==, MBIM_SMS_STATUS_SENT);
+    test_message_trace (pdu_messages[idx]->pdu_data,
+                        pdu_messages[idx]->pdu_data_size,
+                        expected_pdu_idx7,
+                        sizeof (expected_pdu_idx7));
+    g_assert_cmpuint (pdu_messages[idx]->pdu_data_size, ==, sizeof (expected_pdu_idx7));
+    g_assert (memcmp (pdu_messages[idx]->pdu_data, expected_pdu_idx7, sizeof (expected_pdu_idx7)) == 0);
+
+    mbim_sms_pdu_read_record_array_free (pdu_messages);
+    mbim_message_unref (response);
+}
+
+static void
+test_message_parser_ussd (void)
+{
+    MbimUssdResponse ussd_response;
+    MbimUssdSessionState ussd_session_state;
+    const guint8 *ussd_payload;
+    guint32 ussd_payload_size;
+    guint32 ussd_dcs;
+    MbimMessage *response;
+    GError *error = NULL;
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x54, 0x00, 0x00, 0x00, /* length */
+        0x02, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0xE5, 0x50, 0xA0, 0xC8, /* service id */
+        0x5E, 0x82, 0x47, 0x9E,
+        0x82, 0xF7, 0x10, 0xAB,
+        0xF4, 0xC3, 0x35, 0x1F,
+        0x01, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x24, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x05, 0x00, 0x00, 0x00, /* 0x00 response */
+        0x01, 0x00, 0x00, 0x00, /* 0x04 sesstion state */
+        0x01, 0x00, 0x00, 0x00, /* 0x08 coding scheme */
+        0x14, 0x00, 0x00, 0x00, /* 0x0C payload offset */
+        0x10, 0x00, 0x00, 0x00, /* 0x10 payload length */
+        /* data buffer... payload */
+        0x01, 0x02, 0x03, 0x04, /* 0x14 payload */
+        0x05, 0x06, 0x07, 0x08,
+        0x09, 0x0A, 0x0B, 0x0C,
+        0x0D, 0x0E, 0x0F, 0x00
+    };
+
+    const guint8 expected_payload [] = {
+        0x01, 0x02, 0x03, 0x04,
+        0x05, 0x06, 0x07, 0x08,
+        0x09, 0x0A, 0x0B, 0x0C,
+        0x0D, 0x0E, 0x0F, 0x00
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_ussd_response_parse (
+                  response,
+                  &ussd_response,
+                  &ussd_session_state,
+                  &ussd_dcs,
+                  &ussd_payload_size,
+                  &ussd_payload,
+                  &error));
+    g_assert_no_error (error);
+
+    g_assert_cmpuint (ussd_response, ==, MBIM_USSD_RESPONSE_NETWORK_TIMEOUT);
+    g_assert_cmpuint (ussd_session_state, ==, MBIM_USSD_SESSION_STATE_EXISTING_SESSION);
+    g_assert_cmpuint (ussd_dcs, ==, 0x01);
+
+    test_message_trace (ussd_payload,
+                        ussd_payload_size,
+                        expected_payload,
+                        sizeof (expected_payload));
+    g_assert_cmpuint (ussd_payload_size, ==, sizeof (expected_payload));
+    g_assert (memcmp (ussd_payload, expected_payload, sizeof (expected_payload)) == 0);
+
+    mbim_message_unref (response);
+}
+
+static void
+test_message_parser_auth_akap (void)
+{
+    const guint8 *res;
+    guint32 res_len;
+    const guint8 *ik;
+    const guint8 *ck;
+    const guint8 *auts;
+    MbimMessage *response;
+    GError *error = NULL;
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x74, 0x00, 0x00, 0x00, /* length */
+        0x02, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* command_done_message */
+        0x1D, 0x2B, 0x5F, 0xF7, /* service id */
+        0x0A, 0xA1, 0x48, 0xB2,
+        0xAA, 0x52, 0x50, 0xF1,
+        0x57, 0x67, 0x17, 0x4E,
+        0x02, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x44, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x00, 0x01, 0x02, 0x03, /* 0x00 Res */
+        0x04, 0x05, 0x06, 0x07, /* 0x04 */
+        0x08, 0x09, 0x0A, 0x0B, /* 0x08 */
+        0x0C, 0x0D, 0x0E, 0x0F, /* 0x0C */
+        0x05, 0x00, 0x00, 0x00, /* 0x10 Reslen */
+        0xFF, 0xFE, 0xFD, 0xFC, /* 0x14 IK */
+        0xFB, 0xFA, 0xF9, 0xF8, /* 0x18 */
+        0xF7, 0xF6, 0xF5, 0xF4, /* 0x1C */
+        0xF3, 0xF2, 0xF1, 0xF0, /* 0x20 */
+        0xAF, 0xAE, 0xAD, 0xAC, /* 0x24 CK */
+        0xAB, 0xAA, 0xA9, 0xA8, /* 0x28 */
+        0xA7, 0xA6, 0xA5, 0xA4, /* 0x2C */
+        0xA3, 0xA2, 0xA1, 0xA0, /* 0x30 */
+        0x7F, 0x7E, 0x7D, 0x7C, /* 0x34 Auts */
+        0x7B, 0x7A, 0x79, 0x78, /* 0x38 */
+        0x77, 0x76, 0x75, 0x74, /* 0x3C */
+        0x73, 0x72, 0x00, 0x00, /* 0x40 */
+    };
+
+    const guint8 expected_res [] = {
+        0x00, 0x01, 0x02, 0x03,
+        0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B,
+        0x0C, 0x0D, 0x0E, 0x0F,
+    };
+    const guint8 expected_ik [] = {
+        0xFF, 0xFE, 0xFD, 0xFC,
+        0xFB, 0xFA, 0xF9, 0xF8,
+        0xF7, 0xF6, 0xF5, 0xF4,
+        0xF3, 0xF2, 0xF1, 0xF0,
+    };
+    const guint8 expected_ck [] = {
+        0xAF, 0xAE, 0xAD, 0xAC,
+        0xAB, 0xAA, 0xA9, 0xA8,
+        0xA7, 0xA6, 0xA5, 0xA4,
+        0xA3, 0xA2, 0xA1, 0xA0,
+    };
+    const guint8 expected_auts [] = {
+        0x7F, 0x7E, 0x7D, 0x7C,
+        0x7B, 0x7A, 0x79, 0x78,
+        0x77, 0x76, 0x75, 0x74,
+        0x73, 0x72
+    };
+
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_auth_akap_response_parse (
+                  response,
+                  &res,
+                  &res_len,
+                  &ik,
+                  &ck,
+                  &auts,
+                  &error));
+    g_assert_no_error (error);
+
+    test_message_trace (res,
+                        sizeof (expected_res),
+                        expected_res,
+                        sizeof (expected_res));
+    g_assert (memcmp (res, expected_res, sizeof (expected_res)) == 0);
+
+    g_assert_cmpuint (res_len, ==, 5);
+
+    test_message_trace (ik,
+                        sizeof (expected_ik),
+                        expected_ik,
+                        sizeof (expected_ik));
+    g_assert (memcmp (ik, expected_ik, sizeof (expected_ik)) == 0);
+
+    test_message_trace (ck,
+                        sizeof (expected_ck),
+                        expected_ck,
+                        sizeof (expected_ck));
+    g_assert (memcmp (ck, expected_ck, sizeof (expected_ck)) == 0);
+
+    test_message_trace (auts,
+                        sizeof (expected_auts),
+                        expected_auts,
+                        sizeof (expected_auts));
+    g_assert (memcmp (auts, expected_auts, sizeof (expected_auts)) == 0);
+
+    mbim_message_unref (response);
+}
+
+static void
+test_message_parser_stk_pac_notification (void)
+{
+    const guint8 *databuffer;
+    guint32 databuffer_len;
+    guint32 pac_type;
+    MbimMessage *response;
+    GError *error = NULL;
+    const guint8 buffer [] =  {
+        /* header */
+        0x07, 0x00, 0x00, 0x80, /* type */
+        0x54, 0x00, 0x00, 0x00, /* length */
+        0x02, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* indicate_status_message */
+        0xD8, 0xF2, 0x01, 0x31, /* service id */
+        0xFC, 0xB5, 0x4E, 0x17,
+        0x86, 0x02, 0xD6, 0xED,
+        0x38, 0x16, 0x16, 0x4C,
+        0x01, 0x00, 0x00, 0x00, /* command id */
+        0x28, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x01, 0x00, 0x00, 0x00, /* 0x00 Pac Type */
+        0x04, 0x05, 0x06, 0x07, /* 0x04 Data buffer */
+        0xAF, 0xAE, 0xAD, 0xAC,
+        0xAB, 0xAA, 0xA9, 0xA8,
+        0xA7, 0xA6, 0xA5, 0xA4,
+        0xA3, 0xA2, 0xA1, 0xA0,
+        0x7F, 0x7E, 0x7D, 0x7C,
+        0x7B, 0x7A, 0x79, 0x78,
+        0x77, 0x76, 0x75, 0x74,
+        0x73, 0x72, 0x00, 0x00
+    };
+
+    const guint8 expected_databuffer [] = {
+        0x04, 0x05, 0x06, 0x07,
+        0xAF, 0xAE, 0xAD, 0xAC,
+        0xAB, 0xAA, 0xA9, 0xA8,
+        0xA7, 0xA6, 0xA5, 0xA4,
+        0xA3, 0xA2, 0xA1, 0xA0,
+        0x7F, 0x7E, 0x7D, 0x7C,
+        0x7B, 0x7A, 0x79, 0x78,
+        0x77, 0x76, 0x75, 0x74,
+        0x73, 0x72, 0x00, 0x00
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_stk_pac_notification_parse (
+                  response,
+                  &pac_type,
+                  &databuffer_len,
+                  &databuffer,
+                  &error));
+    g_assert_no_error (error);
+
+    g_assert_cmpuint (pac_type, ==, MBIM_STK_PAC_TYPE_NOTIFICATION);
+
+    test_message_trace (databuffer,
+                        sizeof (databuffer_len),
+                        expected_databuffer,
+                        sizeof (expected_databuffer));
+    g_assert_cmpuint (databuffer_len, ==, sizeof (expected_databuffer));
+    g_assert (memcmp (databuffer, expected_databuffer, sizeof (expected_databuffer)) == 0);
+
+    mbim_message_unref (response);
+}
+
+static void
+test_message_parser_stk_pac_response (void)
+{
+    const guint8 *databuffer;
+    MbimMessage *response;
+    GError *error = NULL;
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x2C, 0x01, 0x00, 0x00, /* length */
+        0x02, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* indicate_status_message */
+        0xD8, 0xF2, 0x01, 0x31, /* service id */
+        0xFC, 0xB5, 0x4E, 0x17,
+        0x86, 0x02, 0xD6, 0xED,
+        0x38, 0x16, 0x16, 0x4C,
+        0x01, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x00, 0x01, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x04, 0x05, 0x06, 0x07,
+        0xAF, 0xAE, 0xAD, 0xAC,
+        0xAB, 0xAA, 0xA9, 0xA8,
+        0xA7, 0xA6, 0xA5, 0xA4,
+        0xA3, 0xA2, 0xA1, 0xA0,
+        0x7F, 0x7E, 0x7D, 0x7C,
+        0x7B, 0x7A, 0x79, 0x78,
+        0x77, 0x76, 0x75, 0x74,
+        0x73, 0x72, 0x00, 0x00,
+        0x01, 0x00, 0x00, 0x00,
+        0x04, 0x05, 0x06, 0x07,
+        0xAF, 0xAE, 0xAD, 0xAC,
+        0xAB, 0xAA, 0xA9, 0xA8,
+        0xA7, 0xA6, 0xA5, 0xA4,
+        0xA3, 0xA2, 0xA1, 0xA0,
+        0x7F, 0x7E, 0x7D, 0x7C,
+        0x7B, 0x7A, 0x79, 0x78,
+        0x77, 0x76, 0x75, 0x74,
+        0x73, 0x72, 0x00, 0x00,
+        0x01, 0x00, 0x00, 0x00,
+        0x04, 0x05, 0x06, 0x07,
+        0xAF, 0xAE, 0xAD, 0xAC,
+        0xAB, 0xAA, 0xA9, 0xA8,
+        0xA7, 0xA6, 0xA5, 0xA4,
+        0xA3, 0xA2, 0xA1, 0xA0,
+        0x7F, 0x7E, 0x7D, 0x7C,
+        0x7B, 0x7A, 0x79, 0x78,
+        0x77, 0x76, 0x75, 0x74,
+        0x73, 0x72, 0x00, 0x00,
+        0x01, 0x00, 0x00, 0x00,
+        0x04, 0x05, 0x06, 0x07,
+        0xAF, 0xAE, 0xAD, 0xAC,
+        0xAB, 0xAA, 0xA9, 0xA8,
+        0xA7, 0xA6, 0xA5, 0xA4,
+        0xA3, 0xA2, 0xA1, 0xA0,
+        0x7F, 0x7E, 0x7D, 0x7C,
+        0x7B, 0x7A, 0x79, 0x78,
+        0x77, 0x76, 0x75, 0x74,
+        0x73, 0x72, 0x00, 0x00,
+    };
+
+    const guint8 expected_databuffer [] = {
+        0x04, 0x05, 0x06, 0x07,
+        0xAF, 0xAE, 0xAD, 0xAC,
+        0xAB, 0xAA, 0xA9, 0xA8,
+        0xA7, 0xA6, 0xA5, 0xA4,
+        0xA3, 0xA2, 0xA1, 0xA0,
+        0x7F, 0x7E, 0x7D, 0x7C,
+        0x7B, 0x7A, 0x79, 0x78,
+        0x77, 0x76, 0x75, 0x74,
+        0x73, 0x72, 0x00, 0x00,
+        0x01, 0x00, 0x00, 0x00,
+        0x04, 0x05, 0x06, 0x07,
+        0xAF, 0xAE, 0xAD, 0xAC,
+        0xAB, 0xAA, 0xA9, 0xA8,
+        0xA7, 0xA6, 0xA5, 0xA4,
+        0xA3, 0xA2, 0xA1, 0xA0,
+        0x7F, 0x7E, 0x7D, 0x7C,
+        0x7B, 0x7A, 0x79, 0x78,
+        0x77, 0x76, 0x75, 0x74,
+        0x73, 0x72, 0x00, 0x00,
+        0x01, 0x00, 0x00, 0x00,
+        0x04, 0x05, 0x06, 0x07,
+        0xAF, 0xAE, 0xAD, 0xAC,
+        0xAB, 0xAA, 0xA9, 0xA8,
+        0xA7, 0xA6, 0xA5, 0xA4,
+        0xA3, 0xA2, 0xA1, 0xA0,
+        0x7F, 0x7E, 0x7D, 0x7C,
+        0x7B, 0x7A, 0x79, 0x78,
+        0x77, 0x76, 0x75, 0x74,
+        0x73, 0x72, 0x00, 0x00,
+        0x01, 0x00, 0x00, 0x00,
+        0x04, 0x05, 0x06, 0x07,
+        0xAF, 0xAE, 0xAD, 0xAC,
+        0xAB, 0xAA, 0xA9, 0xA8,
+        0xA7, 0xA6, 0xA5, 0xA4,
+        0xA3, 0xA2, 0xA1, 0xA0,
+        0x7F, 0x7E, 0x7D, 0x7C,
+        0x7B, 0x7A, 0x79, 0x78,
+        0x77, 0x76, 0x75, 0x74,
+        0x73, 0x72, 0x00, 0x00
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_stk_pac_response_parse (
+                  response,
+                  &databuffer,
+                  &error));
+    g_assert_no_error (error);
+
+    test_message_trace (databuffer,
+                        sizeof (expected_databuffer),
+                        expected_databuffer,
+                        sizeof (expected_databuffer));
+    g_assert (memcmp (databuffer, expected_databuffer, sizeof (expected_databuffer)) == 0);
+
+    mbim_message_unref (response);
+}
+
+static void
+test_message_parser_stk_terminal_response (void)
+{
+    const guint8 *databuffer;
+    guint32 databuffer_len;
+    guint32 status_words;
+    MbimMessage *response;
+    GError *error = NULL;
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x48, 0x01, 0x00, 0x00, /* length */
+        0x02, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* indicate_status_message */
+        0xD8, 0xF2, 0x01, 0x31, /* service id */
+        0xFC, 0xB5, 0x4E, 0x17,
+        0x86, 0x02, 0xD6, 0xED,
+        0x38, 0x16, 0x16, 0x4C,
+        0x02, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x18, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x0C, 0x00, 0x00, 0x00, /* 0x00 ResultData offset */
+        0x0C, 0x00, 0x00, 0x00, /* 0x04 ResultData length */
+        0xCC, 0x00, 0x00, 0x00, /* 0x08 StatusWords */
+        /* databuffer */
+        0x00, 0x00, 0x00, 0x00, /* 0x0C ResultData */
+        0x04, 0x05, 0x06, 0x07,
+        0xAF, 0xAE, 0xAD, 0xAC
+    };
+
+    const guint8 expected_databuffer [] = {
+        0x00, 0x00, 0x00, 0x00,
+        0x04, 0x05, 0x06, 0x07,
+        0xAF, 0xAE, 0xAD, 0xAC
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_stk_terminal_response_response_parse (
+                  response,
+                  &databuffer_len,
+                  &databuffer,
+                  &status_words,
+                  &error));
+    g_assert_no_error (error);
+
+    g_assert_cmpuint (status_words, ==, 204);
+
+    test_message_trace (databuffer,
+                        databuffer_len,
+                        expected_databuffer,
+                        sizeof (expected_databuffer));
+    g_assert_cmpuint (databuffer_len, ==, sizeof (expected_databuffer));
+    g_assert (memcmp (databuffer, expected_databuffer, sizeof (expected_databuffer)) == 0);
+
+    mbim_message_unref (response);
+}
+
+static void
+test_message_parser_stk_envelope_response (void)
+{
+    const guint8 *databuffer;
+    MbimMessage *response;
+    GError *error = NULL;
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x50, 0x01, 0x00, 0x00, /* length */
+        0x02, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* indicate_status_message */
+        0xD8, 0xF2, 0x01, 0x31, /* service id */
+        0xFC, 0xB5, 0x4E, 0x17,
+        0x86, 0x02, 0xD6, 0xED,
+        0x38, 0x16, 0x16, 0x4C,
+        0x03, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x20, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x0C, 0x00, 0x00, 0x00,
+        0x0C, 0x00, 0x00, 0x00,
+        0xCC, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x0C, 0x00, 0x00, 0x00,
+        0x0C, 0x00, 0x00, 0x00,
+        0xCC, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00
+    };
+
+    const guint8 expected_databuffer [] = {
+        0x0C, 0x00, 0x00, 0x00,
+        0x0C, 0x00, 0x00, 0x00,
+        0xCC, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x0C, 0x00, 0x00, 0x00,
+        0x0C, 0x00, 0x00, 0x00,
+        0xCC, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_stk_envelope_response_parse (
+                  response,
+                  &databuffer,
+                  &error));
+    g_assert_no_error (error);
+
+    test_message_trace (databuffer,
+                        sizeof (expected_databuffer),
+                        expected_databuffer,
+                        sizeof (expected_databuffer));
+    g_assert (memcmp (databuffer, expected_databuffer, sizeof (expected_databuffer)) == 0);
+
+    mbim_message_unref (response);
+}
+
+static void
+test_message_parser_basic_connect_ip_packet_filters_none (void)
+{
+    MbimPacketFilter **filters = NULL;
+    guint32 n_filters;
+    guint32 session_id;
+    MbimMessage *response;
+    GError *error = NULL;
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x38, 0x01, 0x00, 0x00, /* length */
+        0x02, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* indicate_status_message */
+        0xA2, 0x89, 0xCC, 0x33, /* service id */
+        0xBC, 0xBB, 0x8B, 0x4F,
+        0xB6, 0xB0, 0x13, 0x3E,
+        0xC2, 0xAA, 0xE6, 0xDF,
+        0x17, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x08, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x01, 0x00, 0x00, 0x00, /* session id */
+        0x00, 0x00, 0x00, 0x00  /* packet filters count */
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_ip_packet_filters_response_parse (
+                  response,
+                  &session_id,
+                  &n_filters,
+                  &filters,
+                  &error));
+    g_assert_no_error (error);
+
+    g_assert_cmpuint (session_id, ==, 1);
+    g_assert_cmpuint (n_filters, ==, 0);
+    g_assert (filters == NULL);
+
+    mbim_message_unref (response);
+}
+
+static void
+test_message_parser_basic_connect_ip_packet_filters_one (void)
+{
+    MbimPacketFilter **filters = NULL;
+    guint32 n_filters;
+    guint32 session_id;
+    MbimMessage *response;
+    GError *error = NULL;
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x5C, 0x01, 0x00, 0x00, /* length */
+        0x02, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* indicate_status_message */
+        0xA2, 0x89, 0xCC, 0x33, /* service id */
+        0xBC, 0xBB, 0x8B, 0x4F,
+        0xB6, 0xB0, 0x13, 0x3E,
+        0xC2, 0xAA, 0xE6, 0xDF,
+        0x17, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x2C, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x01, 0x00, 0x00, 0x00, /* 0x00 session id */
+        0x01, 0x00, 0x00, 0x00, /* 0x04 packet filters count */
+        0x10, 0x00, 0x00, 0x00, /* 0x08 packet filter 1 offset */
+        0x1C, 0x00, 0x00, 0x00, /* 0x0C packet filter 1 length */
+        /* databuffer, packet filter 1 */
+        0x08, 0x00, 0x00, 0x00, /* 0x10 0x00 filter size */
+        0x0C, 0x00, 0x00, 0x00, /* 0x14 0x04 filter offset (from beginning of struct) */
+        0x14, 0x00, 0x00, 0x00, /* 0x18 0x08 mask offset (from beginning of struct) */
+        0x01, 0x02, 0x03, 0x04, /* 0x1C 0x0C filter */
+        0x05, 0x06, 0x07, 0x08,
+        0xF1, 0xF2, 0xF3, 0xF4, /* 0x24 0x14 mask */
+        0xF5, 0xF6, 0xF7, 0xF8,
+    };
+
+    const guint8 expected_filter[] = {
+        0x01, 0x02, 0x03, 0x04,
+        0x05, 0x06, 0x07, 0x08,
+    };
+    const guint8 expected_mask[] = {
+        0xF1, 0xF2, 0xF3, 0xF4,
+        0xF5, 0xF6, 0xF7, 0xF8,
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_ip_packet_filters_response_parse (
+                  response,
+                  &session_id,
+                  &n_filters,
+                  &filters,
+                  &error));
+    g_assert_no_error (error);
+
+    g_assert_cmpuint (session_id, ==, 1);
+    g_assert_cmpuint (n_filters, ==, 1);
+    g_assert (filters != NULL);
+
+    g_assert_cmpuint (filters[0]->filter_size, ==, 8);
+
+    test_message_trace (filters[0]->packet_filter, 8,
+                        expected_filter,
+                        sizeof (expected_filter));
+    g_assert (memcmp (filters[0]->packet_filter, expected_filter, sizeof (expected_filter)) == 0);
+
+    test_message_trace (filters[0]->packet_mask, 8,
+                        expected_mask,
+                        sizeof (expected_mask));
+    g_assert (memcmp (filters[0]->packet_mask, expected_mask, sizeof (expected_mask)) == 0);
+
+    mbim_packet_filter_array_free (filters);
+
+    mbim_message_unref (response);
+}
+
+static void
+test_message_parser_basic_connect_ip_packet_filters_two (void)
+{
+    MbimPacketFilter **filters = NULL;
+    guint32 n_filters;
+    guint32 session_id;
+    MbimMessage *response;
+    GError *error = NULL;
+    const guint8 buffer [] =  {
+        /* header */
+        0x03, 0x00, 0x00, 0x80, /* type */
+        0x88, 0x01, 0x00, 0x00, /* length */
+        0x02, 0x00, 0x00, 0x00, /* transaction id */
+        /* fragment header */
+        0x01, 0x00, 0x00, 0x00, /* total */
+        0x00, 0x00, 0x00, 0x00, /* current */
+        /* indicate_status_message */
+        0xA2, 0x89, 0xCC, 0x33, /* service id */
+        0xBC, 0xBB, 0x8B, 0x4F,
+        0xB6, 0xB0, 0x13, 0x3E,
+        0xC2, 0xAA, 0xE6, 0xDF,
+        0x17, 0x00, 0x00, 0x00, /* command id */
+        0x00, 0x00, 0x00, 0x00, /* status code */
+        0x58, 0x00, 0x00, 0x00, /* buffer length */
+        /* information buffer */
+        0x01, 0x00, 0x00, 0x00, /* 0x00 session id */
+        0x02, 0x00, 0x00, 0x00, /* 0x04 packet filters count */
+        0x18, 0x00, 0x00, 0x00, /* 0x08 packet filter 1 offset */
+        0x1C, 0x00, 0x00, 0x00, /* 0x0C packet filter 1 length */
+        0x34, 0x00, 0x00, 0x00, /* 0x10 packet filter 2 offset */
+        0x24, 0x00, 0x00, 0x00, /* 0x14 packet filter 2 length */
+        /* databuffer, packet filter 2 */
+        0x08, 0x00, 0x00, 0x00, /* 0x18 0x00 filter size */
+        0x0C, 0x00, 0x00, 0x00, /* 0x1C 0x04 filter offset (from beginning of struct) */
+        0x14, 0x00, 0x00, 0x00, /* 0x20 0x08 mask offset (from beginning of struct) */
+        0x01, 0x02, 0x03, 0x04, /* 0x24 0x0C filter */
+        0x05, 0x06, 0x07, 0x08,
+        0xF1, 0xF2, 0xF3, 0xF4, /* 0x2C 0x14 mask */
+        0xF5, 0xF6, 0xF7, 0xF8,
+        /* databuffer, packet filter 2 */
+        0x0C, 0x00, 0x00, 0x00, /* 0x34 0x00 filter size */
+        0x0C, 0x00, 0x00, 0x00, /* 0x38 0x04 filter offset (from beginning of struct) */
+        0x18, 0x00, 0x00, 0x00, /* 0x3C 0x08 mask offset (from beginning of struct) */
+        0x01, 0x02, 0x03, 0x04, /* 0x40 0x0C filter */
+        0x05, 0x06, 0x07, 0x08,
+        0x05, 0x06, 0x07, 0x08,
+        0xF1, 0xF2, 0xF3, 0xF4, /* 0x4C 0x18 mask */
+        0xF5, 0xF6, 0xF7, 0xF8,
+        0xF5, 0xF6, 0xF7, 0xF8,
+    };
+
+    const guint8 expected_filter1[] = {
+        0x01, 0x02, 0x03, 0x04,
+        0x05, 0x06, 0x07, 0x08,
+    };
+    const guint8 expected_mask1[] = {
+        0xF1, 0xF2, 0xF3, 0xF4,
+        0xF5, 0xF6, 0xF7, 0xF8,
+    };
+    const guint8 expected_filter2[] = {
+        0x01, 0x02, 0x03, 0x04,
+        0x05, 0x06, 0x07, 0x08,
+        0x05, 0x06, 0x07, 0x08,
+    };
+    const guint8 expected_mask2[] = {
+        0xF1, 0xF2, 0xF3, 0xF4,
+        0xF5, 0xF6, 0xF7, 0xF8,
+        0xF5, 0xF6, 0xF7, 0xF8,
+    };
+
+    response = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert (mbim_message_ip_packet_filters_response_parse (
+                  response,
+                  &session_id,
+                  &n_filters,
+                  &filters,
+                  &error));
+    g_assert_no_error (error);
+
+    g_assert_cmpuint (session_id, ==, 1);
+    g_assert_cmpuint (n_filters, ==, 2);
+    g_assert (filters != NULL);
+
+    g_assert_cmpuint (filters[0]->filter_size, ==, 8);
+    test_message_trace (filters[0]->packet_filter, 8,
+                        expected_filter1,
+                        sizeof (expected_filter1));
+    g_assert (memcmp (filters[0]->packet_filter, expected_filter1, sizeof (expected_filter1)) == 0);
+    test_message_trace (filters[0]->packet_mask, 8,
+                        expected_mask1,
+                        sizeof (expected_mask1));
+    g_assert (memcmp (filters[0]->packet_mask, expected_mask1, sizeof (expected_mask1)) == 0);
+
+    g_assert_cmpuint (filters[1]->filter_size, ==, 12);
+    test_message_trace (filters[1]->packet_filter, 12,
+                        expected_filter2,
+                        sizeof (expected_filter2));
+    g_assert (memcmp (filters[1]->packet_filter, expected_filter2, sizeof (expected_filter2)) == 0);
+    test_message_trace (filters[1]->packet_mask, 12,
+                        expected_mask2,
+                        sizeof (expected_mask2));
+    g_assert (memcmp (filters[1]->packet_mask, expected_mask2, sizeof (expected_mask2)) == 0);
+
+    mbim_packet_filter_array_free (filters);
+
+    mbim_message_unref (response);
+}
+
+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/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);
+
+    return g_test_run ();
+}
diff --git a/libmbim-glib/test/test-message.c b/libmbim-glib/test/test-message.c
new file mode 100644
index 0000000..399ff52
--- /dev/null
+++ b/libmbim-glib/test/test-message.c
@@ -0,0 +1,207 @@
+/* -*- Mode: C; 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 the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "mbim-message.h"
+#include "mbim-cid.h"
+
+static void
+test_message_open (void)
+{
+    MbimMessage *message;
+
+    message = mbim_message_open_new (12345, 4096);
+    g_assert (message != NULL);
+
+    g_assert_cmpuint (mbim_message_get_transaction_id            (message), ==, 12345);
+    g_assert_cmpuint (mbim_message_get_message_type              (message), ==, MBIM_MESSAGE_TYPE_OPEN);
+    g_assert_cmpuint (mbim_message_get_message_length            (message), ==, 16);
+    g_assert_cmpuint (mbim_message_open_get_max_control_transfer (message), ==, 4096);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_open_done (void)
+{
+    MbimMessage *message;
+    const guint8 buffer [] =  { 0x01, 0x00, 0x00, 0x80,
+                                0x10, 0x00, 0x00, 0x00,
+                                0x01, 0x00, 0x00, 0x00,
+                                0x00, 0x00, 0x00, 0x00 };
+
+    message = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert_cmpuint (mbim_message_get_transaction_id        (message), ==, 1);
+    g_assert_cmpuint (mbim_message_get_message_type          (message), ==, MBIM_MESSAGE_TYPE_OPEN_DONE);
+    g_assert_cmpuint (mbim_message_get_message_length        (message), ==, 16);
+    g_assert_cmpuint (mbim_message_open_done_get_status_code (message), ==, MBIM_STATUS_ERROR_NONE);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_close (void)
+{
+    MbimMessage *message;
+
+    message = mbim_message_close_new (12345);
+    g_assert (message != NULL);
+
+    g_assert_cmpuint (mbim_message_get_transaction_id (message), ==, 12345);
+    g_assert_cmpuint (mbim_message_get_message_type   (message), ==, MBIM_MESSAGE_TYPE_CLOSE);
+    g_assert_cmpuint (mbim_message_get_message_length (message), ==, 12);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_close_done (void)
+{
+    MbimMessage *message;
+    const guint8 buffer [] =  { 0x02, 0x00, 0x00, 0x80,
+                                0x10, 0x00, 0x00, 0x00,
+                                0x01, 0x00, 0x00, 0x00,
+                                0x00, 0x00, 0x00, 0x00 };
+
+    message = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert_cmpuint (mbim_message_get_transaction_id         (message), ==, 1);
+    g_assert_cmpuint (mbim_message_get_message_type           (message), ==, MBIM_MESSAGE_TYPE_CLOSE_DONE);
+    g_assert_cmpuint (mbim_message_get_message_length         (message), ==, 16);
+    g_assert_cmpuint (mbim_message_close_done_get_status_code (message), ==, MBIM_STATUS_ERROR_NONE);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_command_empty (void)
+{
+    MbimMessage *message;
+    guint32 len;
+
+    message = mbim_message_command_new (12345,
+                                        MBIM_SERVICE_BASIC_CONNECT,
+                                        MBIM_CID_BASIC_CONNECT_DEVICE_CAPS,
+                                        MBIM_MESSAGE_COMMAND_TYPE_QUERY);
+    g_assert (message != NULL);
+
+    g_assert_cmpuint (mbim_message_get_transaction_id (message), ==, 12345);
+    g_assert_cmpuint (mbim_message_get_message_type   (message), ==, MBIM_MESSAGE_TYPE_COMMAND);
+    g_assert_cmpuint (mbim_message_get_message_length (message), ==, 48);
+
+    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_DEVICE_CAPS);
+    g_assert_cmpuint (mbim_message_command_get_command_type (message), ==, MBIM_MESSAGE_COMMAND_TYPE_QUERY);
+
+    g_assert (mbim_message_command_get_raw_information_buffer (message, &len) == NULL);
+    g_assert_cmpuint (len, ==, 0);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_command_not_empty (void)
+{
+    MbimMessage *message;
+    const guint8 *buffer;
+    guint32 len;
+    const guint8 information_buffer [] = {
+        0x00, 0x01, 0x02, 0x03,
+        0x04, 0x05, 0x06, 0x07
+    };
+
+    message = mbim_message_command_new (12345,
+                                        MBIM_SERVICE_BASIC_CONNECT,
+                                        MBIM_CID_BASIC_CONNECT_DEVICE_CAPS,
+                                        MBIM_MESSAGE_COMMAND_TYPE_QUERY);
+    g_assert (message != NULL);
+    mbim_message_command_append (message, information_buffer, sizeof (information_buffer));
+
+    g_assert_cmpuint (mbim_message_get_transaction_id (message), ==, 12345);
+    g_assert_cmpuint (mbim_message_get_message_type   (message), ==, MBIM_MESSAGE_TYPE_COMMAND);
+    g_assert_cmpuint (mbim_message_get_message_length (message), ==, 56);
+
+    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_DEVICE_CAPS);
+    g_assert_cmpuint (mbim_message_command_get_command_type (message), ==, MBIM_MESSAGE_COMMAND_TYPE_QUERY);
+
+    buffer = mbim_message_command_get_raw_information_buffer (message, &len);
+    g_assert (buffer != NULL);
+    g_assert_cmpuint (len, ==, sizeof (information_buffer));
+    g_assert (memcmp (&information_buffer, buffer, sizeof (information_buffer)) == 0);
+
+    mbim_message_unref (message);
+}
+
+static void
+test_message_command_done (void)
+{
+    MbimMessage *message;
+    const guint8 buffer [] =  { 0x03, 0x00, 0x00, 0x80,
+                                0x3c, 0x00, 0x00, 0x00,
+                                0x01, 0x00, 0x00, 0x00,
+                                0x01, 0x00, 0x00, 0x00,
+                                0x00, 0x00, 0x00, 0x00,
+                                0xa2, 0x89, 0xcc, 0x33,
+                                0xbc, 0xbb, 0x8b, 0x4f,
+                                0xb6, 0xb0, 0x13, 0x3e,
+                                0xc2, 0xaa, 0xe6, 0xdf,
+                                0x04, 0x00, 0x00, 0x00,
+                                0x00, 0x00, 0x00, 0x00,
+                                0x0c, 0x00, 0x00, 0x00,
+                                0x00, 0x00, 0x00, 0x00,
+                                0x00, 0x00, 0x00, 0x00,
+                                0x00, 0x00, 0x00, 0x00 };
+    const guint8 expected_information_buffer [] = { 0x00, 0x00, 0x00, 0x00,
+                                                    0x00, 0x00, 0x00, 0x00,
+                                                    0x00, 0x00, 0x00, 0x00 };
+    const guint8 *out_information_buffer;
+    guint32 len;
+
+    message = mbim_message_new (buffer, sizeof (buffer));
+
+    g_assert_cmpuint (mbim_message_get_transaction_id           (message), ==, 1);
+    g_assert_cmpuint (mbim_message_get_message_type             (message), ==, MBIM_MESSAGE_TYPE_COMMAND_DONE);
+    g_assert_cmpuint (mbim_message_get_message_length           (message), ==, 60);
+
+    g_assert_cmpuint (mbim_message_command_done_get_service     (message), ==, MBIM_SERVICE_BASIC_CONNECT);
+    g_assert_cmpuint (mbim_message_command_done_get_cid         (message), ==, MBIM_CID_BASIC_CONNECT_PIN);
+    g_assert_cmpuint (mbim_message_command_done_get_status_code (message), ==, MBIM_STATUS_ERROR_NONE);
+
+    out_information_buffer = mbim_message_command_done_get_raw_information_buffer (message, &len);
+    g_assert (buffer != NULL);
+    g_assert_cmpuint (len, ==, sizeof (expected_information_buffer));
+    g_assert (memcmp (&expected_information_buffer, out_information_buffer, sizeof (expected_information_buffer)) == 0);
+
+    mbim_message_unref (message);
+}
+
+int main (int argc, char **argv)
+{
+    g_test_init (&argc, &argv, NULL);
+
+    g_test_add_func ("/libmbim-glib/message/open",              test_message_open);
+    g_test_add_func ("/libmbim-glib/message/open-done",         test_message_open_done);
+    g_test_add_func ("/libmbim-glib/message/close",             test_message_close);
+    g_test_add_func ("/libmbim-glib/message/close-done",        test_message_close_done);
+    g_test_add_func ("/libmbim-glib/message/command/empty",     test_message_command_empty);
+    g_test_add_func ("/libmbim-glib/message/command/not-empty", test_message_command_not_empty);
+    g_test_add_func ("/libmbim-glib/message/command-done",      test_message_command_done);
+
+    return g_test_run ();
+}
diff --git a/libmbim-glib/test/test-uuid.c b/libmbim-glib/test/test-uuid.c
new file mode 100644
index 0000000..010b0e9
--- /dev/null
+++ b/libmbim-glib/test/test-uuid.c
@@ -0,0 +1,93 @@
+/* -*- Mode: C; 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 the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include <config.h>
+
+#include "mbim-uuid.h"
+
+static void
+compare_uuid_strings (const MbimUuid *uuid,
+                      const gchar *other_uuid_str)
+{
+    gchar *uuid_str;
+
+    uuid_str = mbim_uuid_get_printable (uuid);
+    g_assert_cmpstr (uuid_str, ==, other_uuid_str);
+    g_free (uuid_str);
+}
+
+static void
+test_uuid_basic_connect (void)
+{
+    compare_uuid_strings (MBIM_UUID_BASIC_CONNECT,
+                          "a289cc33-bcbb-8b4f-b6b0-133ec2aae6df");
+}
+
+static void
+test_uuid_sms (void)
+{
+    compare_uuid_strings (MBIM_UUID_SMS,
+                          "533fbeeb-14fe-4467-9f90-33a223e56c3f");
+}
+
+static void
+test_uuid_ussd (void)
+{
+    compare_uuid_strings (MBIM_UUID_USSD,
+                          "e550a0c8-5e82-479e-82f7-10abf4c3351f");
+}
+
+static void
+test_uuid_phonebook (void)
+{
+    compare_uuid_strings (MBIM_UUID_PHONEBOOK,
+                          "4bf38476-1e6a-41db-b1d8-bed289c25bdb");
+}
+
+static void
+test_uuid_stk (void)
+{
+    compare_uuid_strings (MBIM_UUID_STK,
+                          "d8f20131-fcb5-4e17-8602-d6ed3816164c");
+}
+
+static void
+test_uuid_auth (void)
+{
+    compare_uuid_strings (MBIM_UUID_AUTH,
+                          "1d2b5ff7-0aa1-48b2-aa52-50f15767174e");
+}
+
+static void
+test_uuid_dss (void)
+{
+    compare_uuid_strings (MBIM_UUID_DSS,
+                          "c08a26dd-7718-4382-8482-6e0d583c4d0e");
+}
+
+int main (int argc, char **argv)
+{
+    g_test_init (&argc, &argv, NULL);
+
+    g_test_add_func ("/libmbim-glib/uuid/basic-connect", test_uuid_basic_connect);
+    g_test_add_func ("/libmbim-glib/uuid/sms",           test_uuid_sms);
+    g_test_add_func ("/libmbim-glib/uuid/ussd",          test_uuid_ussd);
+    g_test_add_func ("/libmbim-glib/uuid/phonebook",     test_uuid_phonebook);
+    g_test_add_func ("/libmbim-glib/uuid/stk",           test_uuid_stk);
+    g_test_add_func ("/libmbim-glib/uuid/auth",          test_uuid_auth);
+    g_test_add_func ("/libmbim-glib/uuid/dss",           test_uuid_dss);
+
+    return g_test_run ();
+}
diff --git a/m4/compiler-warnings.m4 b/m4/compiler-warnings.m4
new file mode 100644
index 0000000..eb78503
--- /dev/null
+++ b/m4/compiler-warnings.m4
@@ -0,0 +1,39 @@
+AC_DEFUN([LIBMBIM_COMPILER_WARNINGS],
+[AC_ARG_ENABLE(more-warnings,
+	AS_HELP_STRING([--enable-more-warnings], [Possible values: no/yes/error]),
+	set_more_warnings="$enableval",set_more_warnings=error)
+AC_MSG_CHECKING(for more warnings)
+if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
+	AC_MSG_RESULT(yes)
+	CFLAGS="-Wall -std=gnu89 $CFLAGS"
+
+	for option in -Wmissing-declarations -Wmissing-prototypes \
+		      -Wdeclaration-after-statement -Wstrict-prototypes \
+		      -fno-strict-aliasing -Wno-deprecated-declarations \
+		      -Wint-to-pointer-cast -Wfloat-equal -Wno-unused-parameter \
+		      -Wno-sign-compare -Wunused-but-set-variable \
+		      -Wundef -Wimplicit-function-declaration \
+		      -Wpointer-arith -Winit-self -Wshadow \
+		      -Wmissing-include-dirs -Waggregate-return \
+		      -Wformat-security; do
+		SAVE_CFLAGS="$CFLAGS"
+		CFLAGS="$CFLAGS $option"
+		AC_MSG_CHECKING([whether gcc understands $option])
+		AC_TRY_COMPILE([], [],
+			has_option=yes,
+			has_option=no,)
+		if test $has_option = no; then
+			CFLAGS="$SAVE_CFLAGS"
+		fi
+		AC_MSG_RESULT($has_option)
+		unset has_option
+		unset SAVE_CFLAGS
+	done
+	unset option
+	if test "x$set_more_warnings" = xerror; then
+		CFLAGS="$CFLAGS -Werror"
+	fi
+else
+	AC_MSG_RESULT(no)
+fi
+])
diff --git a/utils/Makefile.am b/utils/Makefile.am
new file mode 100644
index 0000000..bdf3437
--- /dev/null
+++ b/utils/Makefile.am
@@ -0,0 +1,2 @@
+
+dist_bin_SCRIPTS = mbim-network
diff --git a/utils/mbim-network b/utils/mbim-network
new file mode 100755
index 0000000..404d511
--- /dev/null
+++ b/utils/mbim-network
@@ -0,0 +1,259 @@
+#!/bin/sh
+
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, version 2.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+#
+# Based on libqmi's qmi-network script
+#
+
+print_usage ()
+{
+    echo "usage: $0 [DEVICE] [COMMAND]"
+}
+
+if [ $# -ne 2 ]; then
+    echo "error: missing arguments" 1>&2
+    print_usage
+    exit 255
+fi
+
+DEVICE=$1
+COMMAND=$2
+STATE_FILE=/tmp/mbim-network-state
+PROFILE_FILE=/etc/mbim-network.conf
+
+load_profile ()
+{
+    if [ -f $PROFILE_FILE ]; then
+        echo "Loading profile..."
+        source $PROFILE_FILE
+
+        if [ "x$APN" != "x" ]; then
+            echo "    APN: $APN"
+        fi
+    fi
+}
+
+save_state ()
+{
+    KEY=$1
+    VAL=$2
+
+    echo "Saving state... ($KEY: $VAL)"
+
+    if [ -f $STATE_FILE ]; then
+        PREVIOUS=`cat $STATE_FILE`
+        PREVIOUS=`echo "$PREVIOUS" | grep -v $KEY`
+        if [ "x$PREVIOUS" != "x" ]; then
+            echo $PREVIOUS > $STATE_FILE
+        else
+            rm $STATE_FILE
+        fi
+    fi
+
+    if [ "x$VAL" != "x" ]; then
+        echo "$KEY=\"$VAL\"" >> $STATE_FILE
+    fi
+}
+
+load_state ()
+{
+    if [ -f $STATE_FILE ]; then
+        echo "Loading previous state..."
+        source $STATE_FILE
+
+        if [ "x$TRID" != "x" ]; then
+            echo "    Previous Transaction ID: $TRID"
+        fi
+    fi
+}
+
+clear_state ()
+{
+    echo "Clearing state..."
+    rm -f $STATE_FILE
+}
+
+#
+# $ sudo mbimcli -d /dev/cdc-wdm0 --connect="Internet" --no-close
+#   [/dev/cdc-wdm0] Successfully connected
+#   [/dev/cdc-wdm0] Connection status:
+#             Session ID: '0'
+#       Activation state: 'activated'
+#       Voice call state: 'none'
+#                IP type: 'ipv4'
+#           Context type: 'internet'
+#          Network error: 'unknown'
+#
+connect ()
+{
+    # Always try to connect using a fresh session
+    if [ "x$TRID" != "x" ]; then
+        mbimcli -d $DEVICE --no-open=$TRID
+        clear_state
+    fi
+
+    SUBSCRIBER_READY_CMD="mbimcli -d $DEVICE --query-subscriber-ready-status --no-close"
+    echo "Querying subscriber ready status '$SUBSCRIBER_READY_CMD'..."
+
+    SUBSCRIBER_READY_OUT=`$SUBSCRIBER_READY_CMD`
+    echo $SUBSCRIBER_READY_OUT
+
+    # Save the new TRID
+    TRID=`echo "$SUBSCRIBER_READY_OUT" | sed -n "s/.*TRID.*'\(.*\)'.*/\1/p"`
+    if [ "x$TRID" != "x" ]; then
+        save_state "TRID" $TRID
+    fi
+
+
+    REGISTRATION_STATE_CMD="mbimcli -d $DEVICE --query-registration-state --no-open=$TRID --no-close"
+    echo "Querying registration state '$REGISTRATION_STATE_CMD'..."
+
+    REGISTRATION_STATE_OUT=`$REGISTRATION_STATE_CMD`
+    echo $REGISTRATION_STATE_OUT
+
+    # Save the new TRID
+    TRID=`echo "$REGISTRATION_STATE_OUT" | sed -n "s/.*TRID.*'\(.*\)'.*/\1/p"`
+    if [ "x$TRID" != "x" ]; then
+        save_state "TRID" $TRID
+    fi
+
+    ATTACH_CMD="mbimcli -d $DEVICE --attach-packet-service --no-open=$TRID --no-close"
+    echo "Attaching to packet service with '$ATTACH_CMD'..."
+
+    ATTACH_OUT=`$ATTACH_CMD`
+
+    # Save the new TRID
+    TRID=`echo "$ATTACH_OUT" | sed -n "s/.*TRID.*'\(.*\)'.*/\1/p"`
+    if [ "x$TRID" != "x" ]; then
+        save_state "TRID" $TRID
+    fi
+
+    CONNECT_CMD="mbimcli -d $DEVICE --connect=$APN --no-open=$TRID --no-close"
+    echo "Starting network with '$CONNECT_CMD'..."
+
+    CONNECT_OUT=`$CONNECT_CMD`
+    if [ $? -eq 0 ]; then
+        echo "Network started successfully"
+    else
+        echo "Network start failed"
+        echo $CONNECT_OUT
+    fi
+
+    # Save the new TRID
+    TRID=`echo "$CONNECT_OUT" | sed -n "s/.*TRID.*'\(.*\)'.*/\1/p"`
+    if [ "x$TRID" != "x" ]; then
+        save_state "TRID" $TRID
+    fi
+}
+
+#
+# $ sudo mbimcli -d /dev/cdc-wdm0 --disconnect="0"
+#   [/dev/cdc-wdm0] Successfully disconnected
+#   [/dev/cdc-wdm0] Connection status:
+#             Session ID: '0'
+#       Activation state: 'deactivated'
+#       Voice call state: 'none'
+#                IP type: 'default'
+#           Context type: 'internet'
+#          Network error: 'unknown'
+#
+disconnect ()
+{
+    # Always close the session when disconnecting
+    if [ "x$TRID" != "x" ]; then
+        DISCONNECT_CMD="mbimcli -d $DEVICE --disconnect --no-open=$TRID"
+    else
+        DISCONNECT_CMD="mbimcli -d $DEVICE --disconnect"
+    fi
+    echo "Stopping network with '$DISCONNECT_CMD'..."
+
+    DISCONNECT_OUT=`$DISCONNECT_CMD`
+    if [ $? -eq 0 ]; then
+        echo "Network stopped successfully"
+    else
+        echo "Network stop failed"
+        echo $DISCONNECT_OUT
+    fi
+
+    clear_state
+}
+
+#
+# $ sudo mbimcli -d /dev/cdc-wdm0 --query-connection-state  --no-close
+# [/dev/cdc-wdm0] Connection status:
+#         Session ID: '0'
+#   Activation state: 'deactivated'
+#   Voice call state: 'none'
+#            IP type: 'default'
+#       Context type: 'none'
+#      Network error: 'unknown'
+#
+status ()
+{
+    if [ "x$TRID" != "x" ]; then
+        STATUS_CMD="mbimcli -d $DEVICE --query-connection-state --no-close --no-open=$TRID"
+    else
+        STATUS_CMD="mbimcli -d $DEVICE --query-connection-state"
+    fi
+    echo "Getting status with '$STATUS_CMD'..."
+
+    STATUS_OUT=`$STATUS_CMD`
+
+    # Save the new TRID
+    TRID=`echo "$STATUS_OUT" | sed -n "s/.*TRID.*'\(.*\)'.*/\1/p"`
+    if [ "x$TRID" != "x" ]; then
+        save_state "TRID" $TRID
+    fi
+
+    CONN=`echo "$STATUS_OUT" | sed -n "s/.*Activation state:.*'\(.*\)'.*/\1/p"`
+    if [ "x$CONN" = "x" ]; then
+        echo "error: couldn't get connection status" 1>&2
+        exit 2
+    else
+        echo "Status: $CONN"
+        if [ "x$CONN" != "xconnected" ]; then
+            exit 64
+        fi
+    fi
+}
+
+# Main
+
+# Load profile, if any
+load_profile
+
+# Load previous state, if any
+load_state
+
+# Process commands
+case $COMMAND in
+    "start")
+        connect
+        ;;
+    "stop")
+        disconnect
+        ;;
+    "status")
+        status
+        ;;
+    *)
+        echo "error: unexpected command '$COMMAND'" 1>&2
+        print_usage
+        exit 255
+        ;;
+esac
+
+exit 0