diff --git a/AUTHORS b/AUTHORS
index 796b268..643775c 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -145,3 +145,11 @@ Khaled Romdhani <khaledromdhani216@gmail.com>
 Pavel Machek <pavel@ucw.cz>
 Tom Nguyen <tomirq@earthlink.net>
 Stefan Herbrechtsmeier <stefan.herbrechtsmeier@weidmueller.com>
+Lars Poeschel <poeschel@lemonage.de>
+Marius Gripsgard <marius@ubports.com>
+Jimmy Gysens <jimmy.gysens_ext@softathome.com>
+Sergey Matyukevich <geomatsi@gmail.com>
+Shweta Jain <shweta2.jain@intel.com>
+JongSeok Won <wjs890204@gmail.com>
+Sergei Golubtsov <s.e.golubtsov@gmail.com>
+Alexey Andreyev <aa13q@ya.ru>
diff --git a/ChangeLog b/ChangeLog
index 0fc32a8..b40bc6c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+ver 1.34:
+	Fix issue with handling device info of Quectel modems.
+	Fix issue with handling callback for RIL dial manager.
+
+ver 1.33:
+	Fix issue with filling unused part of AID with FFs.
+	Fix issue with reads beyond the first block of SIM filesystem.
+	Fix issue with parsing auth response according to TS 31.102.
+
+ver 1.32:
+	Fix issue with handling of IMS private identity validation.
+	Fix issue with handling of SIM EF structure bit processing.
+	Fix issue with handling removal of Huawai modems.
+	Add support for USSD indication with QMI modems.
+
 ver 1.31:
 	Fix issue with handling CID 0 context identifier.
 	Fix issue with handling detach state and running LTE.
diff --git a/Makefile.am b/Makefile.am
index fbb0eff..2d36346 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,11 +6,11 @@ noinst_LTLIBRARIES =
 if EXTERNAL_ELL
 ell_cflags = @ELL_CFLAGS@
 ell_ldadd = @ELL_LIBS@
-ell_built_sources =
+ell_built_sources = ell/shared
 else
 ell_cflags =
 ell_ldadd = ell/libell-internal.la
-ell_built_sources = ell/internal ell/ell.h
+ell_built_sources = ell/shared ell/internal ell/ell.h
 
 noinst_LTLIBRARIES += ell/libell-internal.la
 
@@ -32,7 +32,8 @@ ell_headers = ell/util.h \
 			ell/file.h \
 			ell/uintset.h \
 			ell/string.h \
-			ell/gpio.h
+			ell/gpio.h \
+			ell/cleanup.h
 
 ell_sources = ell/private.h \
 			ell/missing.h \
@@ -42,6 +43,7 @@ ell_sources = ell/private.h \
 			ell/utf8.c \
 			ell/queue.c \
 			ell/timeout.c \
+			ell/main-private.h \
 			ell/main.c \
 			ell/idle.c \
 			ell/signal.c \
@@ -56,7 +58,9 @@ ell_sources = ell/private.h \
 			ell/string.c \
 			ell/gpio.c
 
-ell_libell_internal_la_SOURCES = $(ell_headers) $(ell_sources)
+ell_shared = ell/useful.h
+
+ell_libell_internal_la_SOURCES = $(ell_headers) $(ell_sources) $(ell_shared)
 endif
 
 pkginclude_HEADERS = include/log.h include/plugin.h include/history.h \
@@ -482,7 +486,10 @@ builtin_sources += drivers/atmodem/atutil.h \
 			drivers/gemaltomodem/gemaltomodem.h \
 			drivers/gemaltomodem/gemaltomodem.c \
 			drivers/gemaltomodem/location-reporting.c \
-			drivers/gemaltomodem/voicecall.c
+			drivers/gemaltomodem/voicecall.c \
+			drivers/gemaltomodem/gprs-context.c \
+			drivers/gemaltomodem/radio-settings.c \
+			drivers/gemaltomodem/netmon.c
 
 builtin_modules += xmm7modem
 builtin_sources += drivers/atmodem/atutil.h \
@@ -603,6 +610,9 @@ builtin_sources += plugins/ublox.c
 builtin_modules += xmm7xxx
 builtin_sources += plugins/xmm7xxx.c
 
+builtin_modules += droid
+builtin_sources += plugins/droid.c
+
 if BLUETOOTH
 if BLUEZ4
 builtin_modules += sap
@@ -696,8 +706,8 @@ builtin_sources += plugins/allowed-apns.c
 
 sbin_PROGRAMS = src/ofonod
 
-src_ofonod_SOURCES = $(builtin_sources) $(gatchat_sources) \
-			linux/gsmmux.h linux/gpio.h src/ofono.ver \
+src_ofonod_SOURCES = $(builtin_sources) $(gatchat_sources) src/ofono.ver \
+			linux/gsmmux.h linux/gpio.h src/missing.h \
 			src/main.c src/ofono.h src/log.c src/plugin.c \
 			src/modem.c src/common.h src/common.c \
 			src/manager.c src/dbus.c src/util.h src/util.c \
@@ -1099,6 +1109,14 @@ include/ofono/%.h: $(abs_top_srcdir)/include/%.h
 	$(AM_V_at)$(MKDIR_P) include/ofono
 	$(AM_V_GEN)$(LN_S) $< $@
 
+ell/shared: Makefile
+	$(AM_V_at)$(MKDIR_P) ell
+	$(AM_V_GEN)for f in $(ell_shared) ; do \
+		if [ ! -f $$f ] ; then \
+			$(LN_S) -t ell -f $(abs_srcdir)/../ell/$$f ; \
+		fi \
+	done > $@
+
 ell/internal: Makefile
 	$(AM_V_at)$(MKDIR_P) ell
 	$(AM_V_GEN)for f in $(ell_headers) $(ell_sources) ; do \
diff --git a/Makefile.in b/Makefile.in
index 38d7e14..f990f43 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -303,6 +303,9 @@ host_triplet = @host@
 @ATMODEM_TRUE@	drivers/gemaltomodem/gemaltomodem.c \
 @ATMODEM_TRUE@	drivers/gemaltomodem/location-reporting.c \
 @ATMODEM_TRUE@	drivers/gemaltomodem/voicecall.c \
+@ATMODEM_TRUE@	drivers/gemaltomodem/gprs-context.c \
+@ATMODEM_TRUE@	drivers/gemaltomodem/radio-settings.c \
+@ATMODEM_TRUE@	drivers/gemaltomodem/netmon.c \
 @ATMODEM_TRUE@	drivers/atmodem/atutil.h \
 @ATMODEM_TRUE@	drivers/xmm7modem/xmm7modem.h \
 @ATMODEM_TRUE@	drivers/xmm7modem/xmm7modem.c \
@@ -323,7 +326,8 @@ host_triplet = @host@
 @ATMODEM_TRUE@	sierra novatel palmpre ifx ste stemgr caif \
 @ATMODEM_TRUE@	cinterion gemalto nokia nokiacdma linktop icera \
 @ATMODEM_TRUE@	alcatel speedup speedupcdma samsung sim900 \
-@ATMODEM_TRUE@	sim7100 connman telit quectel ublox xmm7xxx
+@ATMODEM_TRUE@	sim7100 connman telit quectel ublox xmm7xxx \
+@ATMODEM_TRUE@	droid
 @ATMODEM_TRUE@am__append_22 = plugins/g1.c plugins/wavecom.c \
 @ATMODEM_TRUE@	plugins/calypso.c plugins/mbm.c plugins/hso.c \
 @ATMODEM_TRUE@	plugins/zte.c plugins/huawei.c plugins/sierra.c \
@@ -337,7 +341,8 @@ host_triplet = @host@
 @ATMODEM_TRUE@	plugins/samsung.c plugins/sim900.c \
 @ATMODEM_TRUE@	plugins/sim7100.c plugins/connman.c \
 @ATMODEM_TRUE@	plugins/telit.c plugins/quectel.c \
-@ATMODEM_TRUE@	plugins/ublox.c plugins/xmm7xxx.c
+@ATMODEM_TRUE@	plugins/ublox.c plugins/xmm7xxx.c \
+@ATMODEM_TRUE@	plugins/droid.c
 @ATMODEM_TRUE@@BLUETOOTH_TRUE@@BLUEZ4_TRUE@am__append_23 = sap \
 @ATMODEM_TRUE@@BLUETOOTH_TRUE@@BLUEZ4_TRUE@	hfp_bluez4 \
 @ATMODEM_TRUE@@BLUETOOTH_TRUE@@BLUEZ4_TRUE@	dun_gw_bluez4
@@ -442,11 +447,12 @@ am__ell_libell_internal_la_SOURCES_DIST = ell/util.h ell/test.h \
 	ell/strv.h ell/utf8.h ell/queue.h ell/timeout.h ell/main.h \
 	ell/idle.h ell/signal.h ell/io.h ell/log.h ell/checksum.h \
 	ell/random.h ell/uuid.h ell/file.h ell/uintset.h ell/string.h \
-	ell/gpio.h ell/private.h ell/missing.h ell/util.c ell/test.c \
-	ell/strv.c ell/utf8.c ell/queue.c ell/timeout.c ell/main.c \
-	ell/idle.c ell/signal.c ell/io.c ell/log.c ell/checksum.c \
-	ell/random.c ell/uuid.c ell/file.c ell/uintset.c ell/string.c \
-	ell/gpio.c
+	ell/gpio.h ell/cleanup.h ell/private.h ell/missing.h \
+	ell/util.c ell/test.c ell/strv.c ell/utf8.c ell/queue.c \
+	ell/timeout.c ell/main-private.h ell/main.c ell/idle.c \
+	ell/signal.c ell/io.c ell/log.c ell/checksum.c ell/random.c \
+	ell/uuid.c ell/file.c ell/uintset.c ell/string.c ell/gpio.c \
+	ell/useful.h
 am__objects_1 =
 am__dirstamp = $(am__leading_dot)dirstamp
 @EXTERNAL_ELL_FALSE@am__objects_2 = ell/util.lo ell/test.lo \
@@ -457,7 +463,8 @@ am__dirstamp = $(am__leading_dot)dirstamp
 @EXTERNAL_ELL_FALSE@	ell/uuid.lo ell/file.lo ell/uintset.lo \
 @EXTERNAL_ELL_FALSE@	ell/string.lo ell/gpio.lo
 @EXTERNAL_ELL_FALSE@am_ell_libell_internal_la_OBJECTS =  \
-@EXTERNAL_ELL_FALSE@	$(am__objects_1) $(am__objects_2)
+@EXTERNAL_ELL_FALSE@	$(am__objects_1) $(am__objects_2) \
+@EXTERNAL_ELL_FALSE@	$(am__objects_1)
 ell_libell_internal_la_OBJECTS = $(am_ell_libell_internal_la_OBJECTS)
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -670,7 +677,10 @@ am__src_ofonod_SOURCES_DIST = plugins/udevng.c gril/gril.h gril/gril.c \
 	drivers/gemaltomodem/gemaltomodem.h \
 	drivers/gemaltomodem/gemaltomodem.c \
 	drivers/gemaltomodem/location-reporting.c \
-	drivers/gemaltomodem/voicecall.c drivers/xmm7modem/xmm7modem.h \
+	drivers/gemaltomodem/voicecall.c \
+	drivers/gemaltomodem/gprs-context.c \
+	drivers/gemaltomodem/radio-settings.c \
+	drivers/gemaltomodem/netmon.c drivers/xmm7modem/xmm7modem.h \
 	drivers/xmm7modem/xmm7modem.c \
 	drivers/xmm7modem/radio-settings.c drivers/xmm7modem/ims.c \
 	drivers/xmm7modem/netmon.c plugins/phonesim.c \
@@ -686,9 +696,10 @@ am__src_ofonod_SOURCES_DIST = plugins/udevng.c gril/gril.h gril/gril.c \
 	plugins/speedupcdma.c plugins/samsung.c plugins/sim900.c \
 	plugins/sim7100.c plugins/connman.c plugins/telit.c \
 	plugins/quectel.c plugins/ublox.c plugins/xmm7xxx.c \
-	plugins/sap.c plugins/bluez4.h plugins/hfp_hf_bluez4.c \
-	plugins/dun_gw_bluez4.c plugins/hfp_hf_bluez5.c \
-	plugins/bluez5.h plugins/dun_gw_bluez5.c plugins/bluez4.c \
+	plugins/droid.c plugins/sap.c plugins/bluez4.h \
+	plugins/hfp_hf_bluez4.c plugins/dun_gw_bluez4.c \
+	plugins/hfp_hf_bluez5.c plugins/bluez5.h \
+	plugins/dun_gw_bluez5.c plugins/bluez4.c \
 	plugins/hfp_ag_bluez4.c btio/btio.h btio/btio.c \
 	plugins/bluez5.c plugins/hfp_ag_bluez5.c plugins/upower.c \
 	plugins/mbpi.h plugins/mbpi.c plugins/provision.c \
@@ -709,14 +720,14 @@ am__src_ofonod_SOURCES_DIST = plugins/udevng.c gril/gril.h gril/gril.c \
 	gatchat/gathdlc.h gatchat/gatppp.c gatchat/gatppp.h \
 	gatchat/ppp.h gatchat/ppp_cp.h gatchat/ppp_cp.c \
 	gatchat/ppp_lcp.c gatchat/ppp_auth.c gatchat/ppp_net.c \
-	gatchat/ppp_ipcp.c gatchat/ppp_ipv6cp.c linux/gsmmux.h \
-	linux/gpio.h src/ofono.ver src/main.c src/ofono.h src/log.c \
-	src/plugin.c src/modem.c src/common.h src/common.c \
-	src/manager.c src/dbus.c src/util.h src/util.c src/network.c \
-	src/voicecall.c src/ussd.c src/sms.c src/call-settings.c \
-	src/call-forwarding.c src/call-meter.c src/smsutil.h \
-	src/smsutil.c src/call-barring.c src/sim.c src/stk.c \
-	src/phonebook.c src/history.c src/message-waiting.c \
+	gatchat/ppp_ipcp.c gatchat/ppp_ipv6cp.c src/ofono.ver \
+	linux/gsmmux.h linux/gpio.h src/missing.h src/main.c \
+	src/ofono.h src/log.c src/plugin.c src/modem.c src/common.h \
+	src/common.c src/manager.c src/dbus.c src/util.h src/util.c \
+	src/network.c src/voicecall.c src/ussd.c src/sms.c \
+	src/call-settings.c src/call-forwarding.c src/call-meter.c \
+	src/smsutil.h src/smsutil.c src/call-barring.c src/sim.c \
+	src/stk.c src/phonebook.c src/history.c src/message-waiting.c \
 	src/simutil.h src/simutil.c src/storage.h src/storage.c \
 	src/cbs.c src/watch.c src/call-volume.c src/gprs.c \
 	src/radio-settings.c src/stkutil.h src/stkutil.c src/nettime.c \
@@ -899,6 +910,9 @@ am__objects_9 = gisi/client.$(OBJEXT) gisi/iter.$(OBJEXT) \
 @ATMODEM_TRUE@	drivers/gemaltomodem/gemaltomodem.$(OBJEXT) \
 @ATMODEM_TRUE@	drivers/gemaltomodem/location-reporting.$(OBJEXT) \
 @ATMODEM_TRUE@	drivers/gemaltomodem/voicecall.$(OBJEXT) \
+@ATMODEM_TRUE@	drivers/gemaltomodem/gprs-context.$(OBJEXT) \
+@ATMODEM_TRUE@	drivers/gemaltomodem/radio-settings.$(OBJEXT) \
+@ATMODEM_TRUE@	drivers/gemaltomodem/netmon.$(OBJEXT) \
 @ATMODEM_TRUE@	drivers/xmm7modem/xmm7modem.$(OBJEXT) \
 @ATMODEM_TRUE@	drivers/xmm7modem/radio-settings.$(OBJEXT) \
 @ATMODEM_TRUE@	drivers/xmm7modem/ims.$(OBJEXT) \
@@ -935,7 +949,8 @@ am__objects_9 = gisi/client.$(OBJEXT) gisi/iter.$(OBJEXT) \
 @ATMODEM_TRUE@	plugins/telit.$(OBJEXT) \
 @ATMODEM_TRUE@	plugins/quectel.$(OBJEXT) \
 @ATMODEM_TRUE@	plugins/ublox.$(OBJEXT) \
-@ATMODEM_TRUE@	plugins/xmm7xxx.$(OBJEXT)
+@ATMODEM_TRUE@	plugins/xmm7xxx.$(OBJEXT) \
+@ATMODEM_TRUE@	plugins/droid.$(OBJEXT)
 @ATMODEM_TRUE@@BLUETOOTH_TRUE@@BLUEZ4_TRUE@am__objects_19 = plugins/sap.$(OBJEXT) \
 @ATMODEM_TRUE@@BLUETOOTH_TRUE@@BLUEZ4_TRUE@	plugins/hfp_hf_bluez4.$(OBJEXT) \
 @ATMODEM_TRUE@@BLUETOOTH_TRUE@@BLUEZ4_TRUE@	plugins/dun_gw_bluez4.$(OBJEXT)
@@ -1205,7 +1220,10 @@ am__depfiles_remade = btio/$(DEPDIR)/btio.Po \
 	drivers/dunmodem/$(DEPDIR)/gprs.Po \
 	drivers/dunmodem/$(DEPDIR)/network-registration.Po \
 	drivers/gemaltomodem/$(DEPDIR)/gemaltomodem.Po \
+	drivers/gemaltomodem/$(DEPDIR)/gprs-context.Po \
 	drivers/gemaltomodem/$(DEPDIR)/location-reporting.Po \
+	drivers/gemaltomodem/$(DEPDIR)/netmon.Po \
+	drivers/gemaltomodem/$(DEPDIR)/radio-settings.Po \
 	drivers/gemaltomodem/$(DEPDIR)/voicecall.Po \
 	drivers/hfpmodem/$(DEPDIR)/call-volume.Po \
 	drivers/hfpmodem/$(DEPDIR)/devinfo.Po \
@@ -1373,7 +1391,7 @@ am__depfiles_remade = btio/$(DEPDIR)/btio.Po \
 	plugins/$(DEPDIR)/calypso.Po \
 	plugins/$(DEPDIR)/cdma-provision.Po \
 	plugins/$(DEPDIR)/cinterion.Po plugins/$(DEPDIR)/connman.Po \
-	plugins/$(DEPDIR)/dun_gw_bluez4.Po \
+	plugins/$(DEPDIR)/droid.Po plugins/$(DEPDIR)/dun_gw_bluez4.Po \
 	plugins/$(DEPDIR)/dun_gw_bluez5.Po \
 	plugins/$(DEPDIR)/emulator_fuzz.Po \
 	plugins/$(DEPDIR)/file-provision.Po plugins/$(DEPDIR)/g1.Po \
@@ -1517,8 +1535,8 @@ am__dist_dbusconf_DATA_DIST = src/ofono.conf dundee/dundee.conf
 DATA = $(dist_conf_DATA) $(dist_dbusconf_DATA) $(pkgconfig_DATA) \
 	$(state_DATA) $(systemdunit_DATA)
 HEADERS = $(nodist_pkginclude_HEADERS) $(pkginclude_HEADERS)
-am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
-	$(LISP)config.h.in
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \
+	config.h.in
 # Read a list of newline-separated strings from the standard input,
 # and print each of them once, without duplicates.  Input order is
 # *not* preserved.
@@ -1694,6 +1712,7 @@ am__set_TESTS_bases = \
   bases='$(TEST_LOGS)'; \
   bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
   bases=`echo $$bases`
+AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)'
 RECHECK_LOGS = $(TEST_LOGS)
 TEST_SUITE_LOG = test-suite.log
 TEST_EXTENSIONS = @EXEEXT@ .test
@@ -1735,6 +1754,8 @@ am__post_remove_distdir = $(am__remove_distdir)
 DIST_ARCHIVES = $(distdir).tar.gz
 GZIP_ENV = --best
 DIST_TARGETS = dist-gzip
+# Exists only to be overridden by the user if desired.
+AM_DISTCHECK_DVI_TARGET = dvi
 distuninstallcheck_listfiles = find . -type f -print
 am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
   | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
@@ -1879,8 +1900,8 @@ noinst_LTLIBRARIES = $(am__append_1) gdbus/libgdbus-internal.la
 @EXTERNAL_ELL_TRUE@ell_cflags = @ELL_CFLAGS@
 @EXTERNAL_ELL_FALSE@ell_ldadd = ell/libell-internal.la
 @EXTERNAL_ELL_TRUE@ell_ldadd = @ELL_LIBS@
-@EXTERNAL_ELL_FALSE@ell_built_sources = ell/internal ell/ell.h
-@EXTERNAL_ELL_TRUE@ell_built_sources = 
+@EXTERNAL_ELL_FALSE@ell_built_sources = ell/shared ell/internal ell/ell.h
+@EXTERNAL_ELL_TRUE@ell_built_sources = ell/shared
 @EXTERNAL_ELL_FALSE@ell_headers = ell/util.h \
 @EXTERNAL_ELL_FALSE@			ell/test.h \
 @EXTERNAL_ELL_FALSE@			ell/strv.h \
@@ -1899,7 +1920,8 @@ noinst_LTLIBRARIES = $(am__append_1) gdbus/libgdbus-internal.la
 @EXTERNAL_ELL_FALSE@			ell/file.h \
 @EXTERNAL_ELL_FALSE@			ell/uintset.h \
 @EXTERNAL_ELL_FALSE@			ell/string.h \
-@EXTERNAL_ELL_FALSE@			ell/gpio.h
+@EXTERNAL_ELL_FALSE@			ell/gpio.h \
+@EXTERNAL_ELL_FALSE@			ell/cleanup.h
 
 @EXTERNAL_ELL_FALSE@ell_sources = ell/private.h \
 @EXTERNAL_ELL_FALSE@			ell/missing.h \
@@ -1909,6 +1931,7 @@ noinst_LTLIBRARIES = $(am__append_1) gdbus/libgdbus-internal.la
 @EXTERNAL_ELL_FALSE@			ell/utf8.c \
 @EXTERNAL_ELL_FALSE@			ell/queue.c \
 @EXTERNAL_ELL_FALSE@			ell/timeout.c \
+@EXTERNAL_ELL_FALSE@			ell/main-private.h \
 @EXTERNAL_ELL_FALSE@			ell/main.c \
 @EXTERNAL_ELL_FALSE@			ell/idle.c \
 @EXTERNAL_ELL_FALSE@			ell/signal.c \
@@ -1923,7 +1946,8 @@ noinst_LTLIBRARIES = $(am__append_1) gdbus/libgdbus-internal.la
 @EXTERNAL_ELL_FALSE@			ell/string.c \
 @EXTERNAL_ELL_FALSE@			ell/gpio.c
 
-@EXTERNAL_ELL_FALSE@ell_libell_internal_la_SOURCES = $(ell_headers) $(ell_sources)
+@EXTERNAL_ELL_FALSE@ell_shared = ell/useful.h
+@EXTERNAL_ELL_FALSE@ell_libell_internal_la_SOURCES = $(ell_headers) $(ell_sources) $(ell_shared)
 pkginclude_HEADERS = include/log.h include/plugin.h include/history.h \
 			include/dbus.h include/modem.h include/types.h \
 			include/call-barring.h include/call-forwarding.h \
@@ -2042,8 +2066,8 @@ btio_sources = btio/btio.h btio/btio.c
 @MBIMMODEM_TRUE@		drivers/mbimmodem/mbim-message.h \
 @MBIMMODEM_TRUE@		drivers/mbimmodem/mbim-message.c
 
-src_ofonod_SOURCES = $(builtin_sources) $(gatchat_sources) \
-			linux/gsmmux.h linux/gpio.h src/ofono.ver \
+src_ofonod_SOURCES = $(builtin_sources) $(gatchat_sources) src/ofono.ver \
+			linux/gsmmux.h linux/gpio.h src/missing.h \
 			src/main.c src/ofono.h src/log.c src/plugin.c \
 			src/modem.c src/common.h src/common.c \
 			src/manager.c src/dbus.c src/util.h src/util.c \
@@ -3216,6 +3240,15 @@ drivers/gemaltomodem/location-reporting.$(OBJEXT):  \
 drivers/gemaltomodem/voicecall.$(OBJEXT):  \
 	drivers/gemaltomodem/$(am__dirstamp) \
 	drivers/gemaltomodem/$(DEPDIR)/$(am__dirstamp)
+drivers/gemaltomodem/gprs-context.$(OBJEXT):  \
+	drivers/gemaltomodem/$(am__dirstamp) \
+	drivers/gemaltomodem/$(DEPDIR)/$(am__dirstamp)
+drivers/gemaltomodem/radio-settings.$(OBJEXT):  \
+	drivers/gemaltomodem/$(am__dirstamp) \
+	drivers/gemaltomodem/$(DEPDIR)/$(am__dirstamp)
+drivers/gemaltomodem/netmon.$(OBJEXT):  \
+	drivers/gemaltomodem/$(am__dirstamp) \
+	drivers/gemaltomodem/$(DEPDIR)/$(am__dirstamp)
 drivers/xmm7modem/$(am__dirstamp):
 	@$(MKDIR_P) drivers/xmm7modem
 	@: > drivers/xmm7modem/$(am__dirstamp)
@@ -3314,6 +3347,8 @@ plugins/ublox.$(OBJEXT): plugins/$(am__dirstamp) \
 	plugins/$(DEPDIR)/$(am__dirstamp)
 plugins/xmm7xxx.$(OBJEXT): plugins/$(am__dirstamp) \
 	plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/droid.$(OBJEXT): plugins/$(am__dirstamp) \
+	plugins/$(DEPDIR)/$(am__dirstamp)
 plugins/sap.$(OBJEXT): plugins/$(am__dirstamp) \
 	plugins/$(DEPDIR)/$(am__dirstamp)
 plugins/hfp_hf_bluez4.$(OBJEXT): plugins/$(am__dirstamp) \
@@ -3715,7 +3750,10 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@drivers/dunmodem/$(DEPDIR)/gprs.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@drivers/dunmodem/$(DEPDIR)/network-registration.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@drivers/gemaltomodem/$(DEPDIR)/gemaltomodem.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/gemaltomodem/$(DEPDIR)/gprs-context.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@drivers/gemaltomodem/$(DEPDIR)/location-reporting.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/gemaltomodem/$(DEPDIR)/netmon.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@drivers/gemaltomodem/$(DEPDIR)/radio-settings.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@drivers/gemaltomodem/$(DEPDIR)/voicecall.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@drivers/hfpmodem/$(DEPDIR)/call-volume.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@drivers/hfpmodem/$(DEPDIR)/devinfo.Po@am__quote@ # am--include-marker
@@ -3920,6 +3958,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/cdma-provision.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/cinterion.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/connman.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/droid.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/dun_gw_bluez4.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/dun_gw_bluez5.Po@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/emulator_fuzz.Po@am__quote@ # am--include-marker
@@ -4456,7 +4495,7 @@ $(TEST_SUITE_LOG): $(TEST_LOGS)
 	  test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG);		\
 	fi;								\
 	echo "$${col}$$br$${std}"; 					\
-	echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}";	\
+	echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}";	\
 	echo "$${col}$$br$${std}"; 					\
 	create_testsuite_report --maybe-color;				\
 	echo "$$col$$br$$std";						\
@@ -4640,6 +4679,10 @@ dist-xz: distdir
 	tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
 	$(am__post_remove_distdir)
 
+dist-zstd: distdir
+	tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst
+	$(am__post_remove_distdir)
+
 dist-tarZ: distdir
 	@echo WARNING: "Support for distribution archives compressed with" \
 		       "legacy program 'compress' is deprecated." >&2
@@ -4682,6 +4725,8 @@ distcheck: dist
 	  eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\
 	*.zip*) \
 	  unzip $(distdir).zip ;;\
+	*.tar.zst*) \
+	  zstd -dc $(distdir).tar.zst | $(am__untar) ;;\
 	esac
 	chmod -R a-w $(distdir)
 	chmod u+w $(distdir)
@@ -4697,7 +4742,7 @@ distcheck: dist
 	    $(DISTCHECK_CONFIGURE_FLAGS) \
 	    --srcdir=../.. --prefix="$$dc_install_base" \
 	  && $(MAKE) $(AM_MAKEFLAGS) \
-	  && $(MAKE) $(AM_MAKEFLAGS) dvi \
+	  && $(MAKE) $(AM_MAKEFLAGS) $(AM_DISTCHECK_DVI_TARGET) \
 	  && $(MAKE) $(AM_MAKEFLAGS) check \
 	  && $(MAKE) $(AM_MAKEFLAGS) install \
 	  && $(MAKE) $(AM_MAKEFLAGS) installcheck \
@@ -4760,7 +4805,8 @@ installdirs:
 	done
 install: $(BUILT_SOURCES)
 	$(MAKE) $(AM_MAKEFLAGS) install-am
-install-exec: install-exec-am
+install-exec: $(BUILT_SOURCES)
+	$(MAKE) $(AM_MAKEFLAGS) install-exec-am
 install-data: install-data-am
 uninstall: uninstall-am
 
@@ -4905,7 +4951,10 @@ distclean: distclean-am
 	-rm -f drivers/dunmodem/$(DEPDIR)/gprs.Po
 	-rm -f drivers/dunmodem/$(DEPDIR)/network-registration.Po
 	-rm -f drivers/gemaltomodem/$(DEPDIR)/gemaltomodem.Po
+	-rm -f drivers/gemaltomodem/$(DEPDIR)/gprs-context.Po
 	-rm -f drivers/gemaltomodem/$(DEPDIR)/location-reporting.Po
+	-rm -f drivers/gemaltomodem/$(DEPDIR)/netmon.Po
+	-rm -f drivers/gemaltomodem/$(DEPDIR)/radio-settings.Po
 	-rm -f drivers/gemaltomodem/$(DEPDIR)/voicecall.Po
 	-rm -f drivers/hfpmodem/$(DEPDIR)/call-volume.Po
 	-rm -f drivers/hfpmodem/$(DEPDIR)/devinfo.Po
@@ -5110,6 +5159,7 @@ distclean: distclean-am
 	-rm -f plugins/$(DEPDIR)/cdma-provision.Po
 	-rm -f plugins/$(DEPDIR)/cinterion.Po
 	-rm -f plugins/$(DEPDIR)/connman.Po
+	-rm -f plugins/$(DEPDIR)/droid.Po
 	-rm -f plugins/$(DEPDIR)/dun_gw_bluez4.Po
 	-rm -f plugins/$(DEPDIR)/dun_gw_bluez5.Po
 	-rm -f plugins/$(DEPDIR)/emulator_fuzz.Po
@@ -5325,7 +5375,10 @@ maintainer-clean: maintainer-clean-am
 	-rm -f drivers/dunmodem/$(DEPDIR)/gprs.Po
 	-rm -f drivers/dunmodem/$(DEPDIR)/network-registration.Po
 	-rm -f drivers/gemaltomodem/$(DEPDIR)/gemaltomodem.Po
+	-rm -f drivers/gemaltomodem/$(DEPDIR)/gprs-context.Po
 	-rm -f drivers/gemaltomodem/$(DEPDIR)/location-reporting.Po
+	-rm -f drivers/gemaltomodem/$(DEPDIR)/netmon.Po
+	-rm -f drivers/gemaltomodem/$(DEPDIR)/radio-settings.Po
 	-rm -f drivers/gemaltomodem/$(DEPDIR)/voicecall.Po
 	-rm -f drivers/hfpmodem/$(DEPDIR)/call-volume.Po
 	-rm -f drivers/hfpmodem/$(DEPDIR)/devinfo.Po
@@ -5530,6 +5583,7 @@ maintainer-clean: maintainer-clean-am
 	-rm -f plugins/$(DEPDIR)/cdma-provision.Po
 	-rm -f plugins/$(DEPDIR)/cinterion.Po
 	-rm -f plugins/$(DEPDIR)/connman.Po
+	-rm -f plugins/$(DEPDIR)/droid.Po
 	-rm -f plugins/$(DEPDIR)/dun_gw_bluez4.Po
 	-rm -f plugins/$(DEPDIR)/dun_gw_bluez5.Po
 	-rm -f plugins/$(DEPDIR)/emulator_fuzz.Po
@@ -5688,15 +5742,16 @@ uninstall-am: uninstall-dist_confDATA uninstall-dist_dbusconfDATA \
 
 uninstall-man: uninstall-man8
 
-.MAKE: all check check-am install install-am install-strip
+.MAKE: all check check-am install install-am install-exec \
+	install-strip
 
 .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles am--refresh check \
 	check-TESTS check-am clean clean-cscope clean-generic \
 	clean-libtool clean-local clean-noinstLTLIBRARIES \
 	clean-noinstPROGRAMS clean-sbinPROGRAMS cscope cscopelist-am \
 	ctags ctags-am dist dist-all dist-bzip2 dist-gzip dist-lzip \
-	dist-shar dist-tarZ dist-xz dist-zip distcheck distclean \
-	distclean-compile distclean-generic distclean-hdr \
+	dist-shar dist-tarZ dist-xz dist-zip dist-zstd distcheck \
+	distclean distclean-compile distclean-generic distclean-hdr \
 	distclean-libtool distclean-tags distcleancheck distdir \
 	distuninstallcheck dvi dvi-am html html-am info info-am \
 	install install-am install-data install-data-am \
@@ -5736,6 +5791,14 @@ include/ofono/%.h: $(abs_top_srcdir)/include/%.h
 	$(AM_V_at)$(MKDIR_P) include/ofono
 	$(AM_V_GEN)$(LN_S) $< $@
 
+ell/shared: Makefile
+	$(AM_V_at)$(MKDIR_P) ell
+	$(AM_V_GEN)for f in $(ell_shared) ; do \
+		if [ ! -f $$f ] ; then \
+			$(LN_S) -t ell -f $(abs_srcdir)/../ell/$$f ; \
+		fi \
+	done > $@
+
 ell/internal: Makefile
 	$(AM_V_at)$(MKDIR_P) ell
 	$(AM_V_GEN)for f in $(ell_headers) $(ell_sources) ; do \
diff --git a/README b/README
index 45bb2e9..1c53999 100644
--- a/README
+++ b/README
@@ -64,5 +64,8 @@ Information
 Mailing list:
 	ofono@ofono.org
 
+IRC:
+	irc://irc.oftc.net/#ofono
+
 For additional information about the project visit oFono web site:
 	http://www.ofono.org
diff --git a/aclocal.m4 b/aclocal.m4
index fe1d81f..b010f7e 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,6 +1,6 @@
-# generated automatically by aclocal 1.16.1 -*- Autoconf -*-
+# generated automatically by aclocal 1.16.3 -*- Autoconf -*-
 
-# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
 
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -1055,8 +1055,8 @@ int forced_loaded() { return 2;}
 _LT_EOF
       echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD
       $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD
-      echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD
-      $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD
+      echo "$AR cr libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD
+      $AR cr libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD
       echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD
       $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD
       cat > conftest.c << _LT_EOF
@@ -1085,11 +1085,11 @@ _LT_EOF
       # to the OS version, if on x86, and 10.4, the deployment
       # target defaults to 10.4. Don't you love it?
       case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
-	10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
+	10.0,*86*-darwin8*|10.0,*-darwin[[912]]*)
 	  _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
 	10.[[012]][[,.]]*)
 	  _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
-	10.*)
+	10.*|11.*)
 	  _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
       esac
     ;;
@@ -1506,7 +1506,7 @@ need_locks=$enable_libtool_lock
 m4_defun([_LT_PROG_AR],
 [AC_CHECK_TOOLS(AR, [ar], false)
 : ${AR=ar}
-: ${AR_FLAGS=cru}
+: ${AR_FLAGS=cr}
 _LT_DECL([], [AR], [1], [The archiver])
 _LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive])
 
@@ -9074,9 +9074,9 @@ m4_ifndef([_LT_PROG_F77],		[AC_DEFUN([_LT_PROG_F77])])
 m4_ifndef([_LT_PROG_FC],		[AC_DEFUN([_LT_PROG_FC])])
 m4_ifndef([_LT_PROG_CXX],		[AC_DEFUN([_LT_PROG_CXX])])
 
-dnl pkg.m4 - Macros to locate and utilise pkg-config.   -*- Autoconf -*-
-dnl serial 11 (pkg-config-0.29)
-dnl
+# pkg.m4 - Macros to locate and utilise pkg-config.   -*- Autoconf -*-
+# serial 12 (pkg-config-0.29.2)
+
 dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
 dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
 dnl
@@ -9117,7 +9117,7 @@ dnl
 dnl See the "Since" comment for each macro you use to see what version
 dnl of the macros you require.
 m4_defun([PKG_PREREQ],
-[m4_define([PKG_MACROS_VERSION], [0.29])
+[m4_define([PKG_MACROS_VERSION], [0.29.2])
 m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
     [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
 ])dnl PKG_PREREQ
@@ -9218,7 +9218,7 @@ AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
 AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
 
 pkg_failed=no
-AC_MSG_CHECKING([for $1])
+AC_MSG_CHECKING([for $2])
 
 _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
 _PKG_CONFIG([$1][_LIBS], [libs], [$2])
@@ -9228,11 +9228,11 @@ and $1[]_LIBS to avoid the need to call pkg-config.
 See the pkg-config man page for more details.])
 
 if test $pkg_failed = yes; then
-   	AC_MSG_RESULT([no])
+        AC_MSG_RESULT([no])
         _PKG_SHORT_ERRORS_SUPPORTED
         if test $_pkg_short_errors_supported = yes; then
 	        $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
-        else 
+        else
 	        $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
         fi
 	# Put the nasty error message in config.log where it belongs
@@ -9249,7 +9249,7 @@ installed software in a non-standard prefix.
 _PKG_TEXT])[]dnl
         ])
 elif test $pkg_failed = untried; then
-     	AC_MSG_RESULT([no])
+        AC_MSG_RESULT([no])
 	m4_default([$4], [AC_MSG_FAILURE(
 [The pkg-config script could not be found or is too old.  Make sure it
 is in your PATH or set the PKG_CONFIG environment variable to the full
@@ -9350,7 +9350,7 @@ AS_VAR_COPY([$1], [pkg_cv_][$1])
 AS_VAR_IF([$1], [""], [$5], [$4])dnl
 ])dnl PKG_CHECK_VAR
 
-# Copyright (C) 2002-2018 Free Software Foundation, Inc.
+# Copyright (C) 2002-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -9365,7 +9365,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION],
 [am__api_version='1.16'
 dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
 dnl require some minimum version.  Point them to the right macro.
-m4_if([$1], [1.16.1], [],
+m4_if([$1], [1.16.3], [],
       [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
 ])
 
@@ -9381,14 +9381,14 @@ m4_define([_AM_AUTOCONF_VERSION], [])
 # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
 # This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
 AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.16.1])dnl
+[AM_AUTOMAKE_VERSION([1.16.3])dnl
 m4_ifndef([AC_AUTOCONF_VERSION],
   [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
 _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
 
 # AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
 
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -9440,7 +9440,7 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd`
 
 # AM_CONDITIONAL                                            -*- Autoconf -*-
 
-# Copyright (C) 1997-2018 Free Software Foundation, Inc.
+# Copyright (C) 1997-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -9471,7 +9471,7 @@ AC_CONFIG_COMMANDS_PRE(
 Usually this means the macro was only invoked conditionally.]])
 fi])])
 
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -9662,7 +9662,7 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl
 
 # Generate code to set up dependency tracking.              -*- Autoconf -*-
 
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -9701,7 +9701,9 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
   done
   if test $am_rc -ne 0; then
     AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments
-    for automatic dependency tracking.  Try re-running configure with the
+    for automatic dependency tracking.  If GNU make was not used, consider
+    re-running the configure script with MAKE="gmake" (or whatever is
+    necessary).  You can also try re-running configure with the
     '--disable-dependency-tracking' option to at least be able to build
     the package (albeit without support for automatic dependency tracking).])
   fi
@@ -9728,7 +9730,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
 
 # Do all the work for Automake.                             -*- Autoconf -*-
 
-# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -9925,7 +9927,7 @@ for _am_header in $config_headers :; do
 done
 echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
 
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -9946,7 +9948,7 @@ if test x"${install_sh+set}" != xset; then
 fi
 AC_SUBST([install_sh])])
 
-# Copyright (C) 2003-2018 Free Software Foundation, Inc.
+# Copyright (C) 2003-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -9968,7 +9970,7 @@ AC_SUBST([am__leading_dot])])
 # Add --enable-maintainer-mode option to configure.         -*- Autoconf -*-
 # From Jim Meyering
 
-# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -10003,7 +10005,7 @@ AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
 
 # Check to see how 'make' treats includes.	            -*- Autoconf -*-
 
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -10046,7 +10048,7 @@ AC_SUBST([am__quote])])
 
 # Fake the existence of programs that GNU maintainers use.  -*- Autoconf -*-
 
-# Copyright (C) 1997-2018 Free Software Foundation, Inc.
+# Copyright (C) 1997-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -10067,12 +10069,7 @@ AC_DEFUN([AM_MISSING_HAS_RUN],
 [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
 AC_REQUIRE_AUX_FILE([missing])dnl
 if test x"${MISSING+set}" != xset; then
-  case $am_aux_dir in
-  *\ * | *\	*)
-    MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
-  *)
-    MISSING="\${SHELL} $am_aux_dir/missing" ;;
-  esac
+  MISSING="\${SHELL} '$am_aux_dir/missing'"
 fi
 # Use eval to expand $SHELL
 if eval "$MISSING --is-lightweight"; then
@@ -10085,7 +10082,7 @@ fi
 
 # Helper functions for option handling.                     -*- Autoconf -*-
 
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -10114,7 +10111,7 @@ AC_DEFUN([_AM_SET_OPTIONS],
 AC_DEFUN([_AM_IF_OPTION],
 [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
 
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -10161,7 +10158,7 @@ AC_LANG_POP([C])])
 # For backward compatibility.
 AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
 
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -10180,7 +10177,7 @@ AC_DEFUN([AM_RUN_LOG],
 
 # Check to make sure that the build environment is sane.    -*- Autoconf -*-
 
-# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -10261,7 +10258,7 @@ AC_CONFIG_COMMANDS_PRE(
 rm -f conftest.file
 ])
 
-# Copyright (C) 2009-2018 Free Software Foundation, Inc.
+# Copyright (C) 2009-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -10321,7 +10318,7 @@ AC_SUBST([AM_BACKSLASH])dnl
 _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
 ])
 
-# Copyright (C) 2001-2018 Free Software Foundation, Inc.
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -10349,7 +10346,7 @@ fi
 INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
 AC_SUBST([INSTALL_STRIP_PROGRAM])])
 
-# Copyright (C) 2006-2018 Free Software Foundation, Inc.
+# Copyright (C) 2006-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -10368,7 +10365,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
 
 # Check how to create a tarball.                            -*- Autoconf -*-
 
-# Copyright (C) 2004-2018 Free Software Foundation, Inc.
+# Copyright (C) 2004-2020 Free Software Foundation, Inc.
 #
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
diff --git a/compile b/compile
index 99e5052..23fcba0 100755
--- a/compile
+++ b/compile
@@ -3,7 +3,7 @@
 
 scriptversion=2018-03-07.03; # UTC
 
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
 # Written by Tom Tromey <tromey@cygnus.com>.
 #
 # This program is free software; you can redistribute it and/or modify
@@ -53,7 +53,7 @@ func_file_conv ()
 	  MINGW*)
 	    file_conv=mingw
 	    ;;
-	  CYGWIN*)
+	  CYGWIN* | MSYS*)
 	    file_conv=cygwin
 	    ;;
 	  *)
@@ -67,7 +67,7 @@ func_file_conv ()
 	mingw/*)
 	  file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
 	  ;;
-	cygwin/*)
+	cygwin/* | msys/*)
 	  file=`cygpath -m "$file" || echo "$file"`
 	  ;;
 	wine/*)
diff --git a/config.h.in b/config.h.in
index d0b13c7..821d551 100644
--- a/config.h.in
+++ b/config.h.in
@@ -9,12 +9,18 @@
 /* Define to 1 if you have the `explicit_bzero' function. */
 #undef HAVE_EXPLICIT_BZERO
 
+/* Define to 1 if you have the `g_memdup2' function. */
+#undef HAVE_G_MEMDUP2
+
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
 /* Define to 1 if you have the <memory.h> header file. */
 #undef HAVE_MEMORY_H
 
+/* Define to 1 if you have the `rawmemchr' function. */
+#undef HAVE_RAWMEMCHR
+
 /* Define to 1 if you have the <stdint.h> header file. */
 #undef HAVE_STDINT_H
 
diff --git a/configure b/configure
index 7cdf4e4..7137a4b 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for ofono 1.31.
+# Generated by GNU Autoconf 2.69 for ofono 1.34.
 #
 #
 # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -587,8 +587,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='ofono'
 PACKAGE_TARNAME='ofono'
-PACKAGE_VERSION='1.31'
-PACKAGE_STRING='ofono 1.31'
+PACKAGE_VERSION='1.34'
+PACKAGE_STRING='ofono 1.34'
 PACKAGE_BUGREPORT=''
 PACKAGE_URL=''
 
@@ -1415,7 +1415,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures ofono 1.31 to adapt to many kinds of systems.
+\`configure' configures ofono 1.34 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1486,7 +1486,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of ofono 1.31:";;
+     short | recursive ) echo "Configuration of ofono 1.34:";;
    esac
   cat <<\_ACEOF
 
@@ -1640,7 +1640,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-ofono configure 1.31
+ofono configure 1.34
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1918,7 +1918,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by ofono $as_me 1.31, which was
+It was created by ofono $as_me 1.34, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2472,12 +2472,7 @@ program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
 am_aux_dir=`cd "$ac_aux_dir" && pwd`
 
 if test x"${MISSING+set}" != xset; then
-  case $am_aux_dir in
-  *\ * | *\	*)
-    MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
-  *)
-    MISSING="\${SHELL} $am_aux_dir/missing" ;;
-  esac
+  MISSING="\${SHELL} '$am_aux_dir/missing'"
 fi
 # Use eval to expand $SHELL
 if eval "$MISSING --is-lightweight"; then
@@ -2782,7 +2777,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='ofono'
- VERSION='1.31'
+ VERSION='1.34'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -6631,7 +6626,7 @@ esac
 fi
 
 : ${AR=ar}
-: ${AR_FLAGS=cru}
+: ${AR_FLAGS=cr}
 
 
 
@@ -8352,8 +8347,8 @@ int forced_loaded() { return 2;}
 _LT_EOF
       echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5
       $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5
-      echo "$AR cru libconftest.a conftest.o" >&5
-      $AR cru libconftest.a conftest.o 2>&5
+      echo "$AR cr libconftest.a conftest.o" >&5
+      $AR cr libconftest.a conftest.o 2>&5
       echo "$RANLIB libconftest.a" >&5
       $RANLIB libconftest.a 2>&5
       cat > conftest.c << _LT_EOF
@@ -8385,11 +8380,11 @@ $as_echo "$lt_cv_ld_force_load" >&6; }
       # to the OS version, if on x86, and 10.4, the deployment
       # target defaults to 10.4. Don't you love it?
       case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
-	10.0,*86*-darwin8*|10.0,*-darwin[91]*)
+	10.0,*86*-darwin8*|10.0,*-darwin[912]*)
 	  _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
 	10.[012][,.]*)
 	  _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
-	10.*)
+	10.*|11.*)
 	  _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
       esac
     ;;
@@ -13058,6 +13053,17 @@ _ACEOF
 fi
 done
 
+for ac_func in rawmemchr
+do :
+  ac_fn_c_check_func "$LINENO" "rawmemchr" "ac_cv_func_rawmemchr"
+if test "x$ac_cv_func_rawmemchr" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_RAWMEMCHR 1
+_ACEOF
+
+fi
+done
+
 
 ac_fn_c_check_func "$LINENO" "signalfd" "ac_cv_func_signalfd"
 if test "x$ac_cv_func_signalfd" = xyes; then :
@@ -13112,19 +13118,155 @@ fi
 
 
 pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GLIB" >&5
-$as_echo_n "checking for GLIB... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for glib-2.0 >= 2.68" >&5
+$as_echo_n "checking for glib-2.0 >= 2.68... " >&6; }
+
+if test -n "$GLIB_CFLAGS"; then
+    pkg_cv_GLIB_CFLAGS="$GLIB_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.68\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.68") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_GLIB_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.68" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$GLIB_LIBS"; then
+    pkg_cv_GLIB_LIBS="$GLIB_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.68\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.68") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_GLIB_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.68" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+	        GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "glib-2.0 >= 2.68" 2>&1`
+        else
+	        GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "glib-2.0 >= 2.68" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$GLIB_PKG_ERRORS" >&5
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for glib-2.0 >= 2.60" >&5
+$as_echo_n "checking for glib-2.0 >= 2.60... " >&6; }
+
+if test -n "$GLIB_CFLAGS"; then
+    pkg_cv_GLIB_CFLAGS="$GLIB_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.60\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.60") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_GLIB_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.60" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$GLIB_LIBS"; then
+    pkg_cv_GLIB_LIBS="$GLIB_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.60\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.60") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_GLIB_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.60" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+	        GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "glib-2.0 >= 2.60" 2>&1`
+        else
+	        GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "glib-2.0 >= 2.60" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$GLIB_PKG_ERRORS" >&5
+
+	as_fn_error $? "GLib >= 2.60 is required" "$LINENO" 5
+elif test $pkg_failed = untried; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+	as_fn_error $? "GLib >= 2.60 is required" "$LINENO" 5
+else
+	GLIB_CFLAGS=$pkg_cv_GLIB_CFLAGS
+	GLIB_LIBS=$pkg_cv_GLIB_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+	dummy=yes
+fi
+
+elif test $pkg_failed = untried; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for glib-2.0 >= 2.60" >&5
+$as_echo_n "checking for glib-2.0 >= 2.60... " >&6; }
 
 if test -n "$GLIB_CFLAGS"; then
     pkg_cv_GLIB_CFLAGS="$GLIB_CFLAGS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.32\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.32") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.60\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.60") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_GLIB_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.32" 2>/dev/null`
+  pkg_cv_GLIB_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.60" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -13136,12 +13278,12 @@ if test -n "$GLIB_LIBS"; then
     pkg_cv_GLIB_LIBS="$GLIB_LIBS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.32\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.32") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.60\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.60") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_GLIB_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.32" 2>/dev/null`
+  pkg_cv_GLIB_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.60" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -13153,7 +13295,7 @@ fi
 
 
 if test $pkg_failed = yes; then
-   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
 
 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -13162,18 +13304,18 @@ else
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-	        GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "glib-2.0 >= 2.32" 2>&1`
+	        GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "glib-2.0 >= 2.60" 2>&1`
         else
-	        GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "glib-2.0 >= 2.32" 2>&1`
+	        GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "glib-2.0 >= 2.60" 2>&1`
         fi
 	# Put the nasty error message in config.log where it belongs
 	echo "$GLIB_PKG_ERRORS" >&5
 
-	as_fn_error $? "GLib >= 2.32 is required" "$LINENO" 5
+	as_fn_error $? "GLib >= 2.60 is required" "$LINENO" 5
 elif test $pkg_failed = untried; then
-     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-	as_fn_error $? "GLib >= 2.32 is required" "$LINENO" 5
+	as_fn_error $? "GLib >= 2.60 is required" "$LINENO" 5
 else
 	GLIB_CFLAGS=$pkg_cv_GLIB_CFLAGS
 	GLIB_LIBS=$pkg_cv_GLIB_LIBS
@@ -13182,23 +13324,37 @@ $as_echo "yes" >&6; }
 	dummy=yes
 fi
 
+else
+	GLIB_CFLAGS=$pkg_cv_GLIB_CFLAGS
+	GLIB_LIBS=$pkg_cv_GLIB_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_G_MEMDUP2 1
+_ACEOF
+
+
+fi
+
 
 
 
 pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for DBUS" >&5
-$as_echo_n "checking for DBUS... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dbus-1 >= 1.6" >&5
+$as_echo_n "checking for dbus-1 >= 1.6... " >&6; }
 
 if test -n "$DBUS_CFLAGS"; then
     pkg_cv_DBUS_CFLAGS="$DBUS_CFLAGS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.4\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.4") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.6\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.6") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_DBUS_CFLAGS=`$PKG_CONFIG --cflags "dbus-1 >= 1.4" 2>/dev/null`
+  pkg_cv_DBUS_CFLAGS=`$PKG_CONFIG --cflags "dbus-1 >= 1.6" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -13210,12 +13366,12 @@ if test -n "$DBUS_LIBS"; then
     pkg_cv_DBUS_LIBS="$DBUS_LIBS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.4\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.4") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.6\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.6") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_DBUS_LIBS=`$PKG_CONFIG --libs "dbus-1 >= 1.4" 2>/dev/null`
+  pkg_cv_DBUS_LIBS=`$PKG_CONFIG --libs "dbus-1 >= 1.6" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -13227,7 +13383,7 @@ fi
 
 
 if test $pkg_failed = yes; then
-   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
 
 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -13236,18 +13392,18 @@ else
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-	        DBUS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "dbus-1 >= 1.4" 2>&1`
+	        DBUS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "dbus-1 >= 1.6" 2>&1`
         else
-	        DBUS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "dbus-1 >= 1.4" 2>&1`
+	        DBUS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "dbus-1 >= 1.6" 2>&1`
         fi
 	# Put the nasty error message in config.log where it belongs
 	echo "$DBUS_PKG_ERRORS" >&5
 
-	as_fn_error $? "D-Bus >= 1.4 is required" "$LINENO" 5
+	as_fn_error $? "D-Bus >= 1.6 is required" "$LINENO" 5
 elif test $pkg_failed = untried; then
-     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-	as_fn_error $? "D-Bus >= 1.4 is required" "$LINENO" 5
+	as_fn_error $? "D-Bus >= 1.6 is required" "$LINENO" 5
 else
 	DBUS_CFLAGS=$pkg_cv_DBUS_CFLAGS
 	DBUS_LIBS=$pkg_cv_DBUS_LIBS
@@ -13359,8 +13515,8 @@ fi
 if (test "${enable_udev}" != "no"); then
 
 pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for UDEV" >&5
-$as_echo_n "checking for UDEV... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libudev >= 143" >&5
+$as_echo_n "checking for libudev >= 143... " >&6; }
 
 if test -n "$UDEV_CFLAGS"; then
     pkg_cv_UDEV_CFLAGS="$UDEV_CFLAGS"
@@ -13400,7 +13556,7 @@ fi
 
 
 if test $pkg_failed = yes; then
-   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
 
 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -13418,7 +13574,7 @@ fi
 
 	as_fn_error $? "libudev >= 143 is required" "$LINENO" 5
 elif test $pkg_failed = untried; then
-     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
 	as_fn_error $? "libudev >= 143 is required" "$LINENO" 5
 else
@@ -13561,8 +13717,8 @@ fi
 if (test "${enable_bluez4}" = "yes"); then
 
 pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BLUEZ" >&5
-$as_echo_n "checking for BLUEZ... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for bluez >= 4.99 bluez < 5" >&5
+$as_echo_n "checking for bluez >= 4.99 bluez < 5... " >&6; }
 
 if test -n "$BLUEZ_CFLAGS"; then
     pkg_cv_BLUEZ_CFLAGS="$BLUEZ_CFLAGS"
@@ -13602,7 +13758,7 @@ fi
 
 
 if test $pkg_failed = yes; then
-   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
 
 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -13620,7 +13776,7 @@ fi
 
 	as_fn_error $? "Bluetooth library >= 4.99 and < 5 is required" "$LINENO" 5
 elif test $pkg_failed = untried; then
-     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
 	as_fn_error $? "Bluetooth library >= 4.99 and < 5 is required" "$LINENO" 5
 else
@@ -13739,8 +13895,8 @@ fi
 if (test "${enable_external_ell}" = "yes"); then
 
 pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ELL" >&5
-$as_echo_n "checking for ELL... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ell >= 0.12" >&5
+$as_echo_n "checking for ell >= 0.12... " >&6; }
 
 if test -n "$ELL_CFLAGS"; then
     pkg_cv_ELL_CFLAGS="$ELL_CFLAGS"
@@ -13780,7 +13936,7 @@ fi
 
 
 if test $pkg_failed = yes; then
-   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
 
 if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
@@ -13798,7 +13954,7 @@ fi
 
 	as_fn_error $? "Embedded Linux library >= 0.12 is required" "$LINENO" 5
 elif test $pkg_failed = untried; then
-     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
 	as_fn_error $? "Embedded Linux library >= 0.12 is required" "$LINENO" 5
 else
@@ -14477,7 +14633,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by ofono $as_me 1.31, which was
+This file was extended by ofono $as_me 1.34, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -14543,7 +14699,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-ofono config.status 1.31
+ofono config.status 1.34
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
@@ -15640,7 +15796,9 @@ $as_echo X/"$am_mf" |
     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
 as_fn_error $? "Something went wrong bootstrapping makefile fragments
-    for automatic dependency tracking.  Try re-running configure with the
+    for automatic dependency tracking.  If GNU make was not used, consider
+    re-running the configure script with MAKE=\"gmake\" (or whatever is
+    necessary).  You can also try re-running configure with the
     '--disable-dependency-tracking' option to at least be able to build
     the package (albeit without support for automatic dependency tracking).
 See \`config.log' for more details" "$LINENO" 5; }
diff --git a/configure.ac b/configure.ac
index b17bfb5..850b507 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(ofono, 1.31)
+AC_INIT(ofono, 1.34)
 
 AM_INIT_AUTOMAKE([foreign subdir-objects color-tests])
 AC_CONFIG_HEADERS(config.h)
@@ -28,7 +28,7 @@ m4_define([_LT_AC_TAGCONFIG], [])
 m4_ifdef([AC_LIBTOOL_TAGS], [AC_LIBTOOL_TAGS([])])
 
 AC_DISABLE_STATIC
-AC_PROG_LIBTOOL
+LT_INIT
 
 AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization],
 			[disable code optimization through compiler]), [
@@ -55,6 +55,7 @@ AC_ARG_ENABLE(pie, AC_HELP_STRING([--enable-pie],
 ])
 
 AC_CHECK_FUNCS(explicit_bzero)
+AC_CHECK_FUNCS(rawmemchr)
 
 AC_CHECK_FUNC(signalfd, dummy=yes,
 			AC_MSG_ERROR(signalfd support is required))
@@ -62,13 +63,18 @@ AC_CHECK_FUNC(signalfd, dummy=yes,
 AC_CHECK_LIB(dl, dlopen, dummy=yes,
 			AC_MSG_ERROR(dynamic linking loader is required))
 
-PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.32, dummy=yes,
-				AC_MSG_ERROR(GLib >= 2.32 is required))
+PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.68, [
+	AC_DEFINE_UNQUOTED(HAVE_G_MEMDUP2, 1,
+			[Define to 1 if you have the `g_memdup2' function.])
+], [
+	PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.60, dummy=yes,
+				AC_MSG_ERROR(GLib >= 2.60 is required))
+])
 AC_SUBST(GLIB_CFLAGS)
 AC_SUBST(GLIB_LIBS)
 
-PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.4, dummy=yes,
-				AC_MSG_ERROR(D-Bus >= 1.4 is required))
+PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.6, dummy=yes,
+				AC_MSG_ERROR(D-Bus >= 1.6 is required))
 AC_SUBST(DBUS_CFLAGS)
 AC_SUBST(DBUS_LIBS)
 
diff --git a/debian/changelog b/debian/changelog
index c0e19a1..ff40cf8 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+ofono (1.34-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Mon, 16 May 2022 11:26:27 -0000
+
 ofono (1.31-3) unstable; urgency=medium
 
   * Team upload.
diff --git a/debian/patches/0001-doc-ofonod.8-escape-minus-sign.patch b/debian/patches/0001-doc-ofonod.8-escape-minus-sign.patch
index 105bc45..51cb055 100644
--- a/debian/patches/0001-doc-ofonod.8-escape-minus-sign.patch
+++ b/debian/patches/0001-doc-ofonod.8-escape-minus-sign.patch
@@ -10,11 +10,11 @@ Signed-off-by: Jonny Lamb <jonny@debian.org>
  doc/ofonod.8 |    2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
-Index: ofono-1.9/doc/ofonod.8
+Index: ofono/doc/ofonod.8
 ===================================================================
---- ofono-1.9.orig/doc/ofonod.8	2010-08-02 08:17:42.000000000 +0300
-+++ ofono-1.9/doc/ofonod.8	2012-07-31 19:49:47.000000000 +0300
-@@ -18,7 +18,7 @@
+--- ofono.orig/doc/ofonod.8
++++ ofono/doc/ofonod.8
+@@ -18,7 +18,7 @@ is used to manage \fID-Bus\fP permission
  .SH OPTIONS
  .TP
  .B --debug, -d
diff --git a/debian/patches/0002-Remove-After-syslog.target-from-systemd-.service-fil.patch b/debian/patches/0002-Remove-After-syslog.target-from-systemd-.service-fil.patch
index 1bf087b..1fbe934 100644
--- a/debian/patches/0002-Remove-After-syslog.target-from-systemd-.service-fil.patch
+++ b/debian/patches/0002-Remove-After-syslog.target-from-systemd-.service-fil.patch
@@ -7,10 +7,10 @@ Subject: Remove After=syslog.target from systemd .service files
  src/ofono.service.in     | 1 -
  2 files changed, 2 deletions(-)
 
-diff --git a/dundee/dundee.service.in b/dundee/dundee.service.in
-index 82c5ef1..561cdf1 100644
---- a/dundee/dundee.service.in
-+++ b/dundee/dundee.service.in
+Index: ofono/dundee/dundee.service.in
+===================================================================
+--- ofono.orig/dundee/dundee.service.in
++++ ofono/dundee/dundee.service.in
 @@ -1,6 +1,5 @@
  [Unit]
  Description=DUN service
@@ -18,10 +18,10 @@ index 82c5ef1..561cdf1 100644
  
  [Service]
  Type=dbus
-diff --git a/src/ofono.service.in b/src/ofono.service.in
-index c24ac28..25f2d77 100644
---- a/src/ofono.service.in
-+++ b/src/ofono.service.in
+Index: ofono/src/ofono.service.in
+===================================================================
+--- ofono.orig/src/ofono.service.in
++++ ofono/src/ofono.service.in
 @@ -1,6 +1,5 @@
  [Unit]
  Description=Telephony service
diff --git a/depcomp b/depcomp
index 65cbf70..6b39162 100755
--- a/depcomp
+++ b/depcomp
@@ -3,7 +3,7 @@
 
 scriptversion=2018-03-07.03; # UTC
 
-# Copyright (C) 1999-2018 Free Software Foundation, Inc.
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
 
 # 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
diff --git a/doc/networkmonitor-api.txt b/doc/networkmonitor-api.txt
index 1cc73d2..af56f04 100644
--- a/doc/networkmonitor-api.txt
+++ b/doc/networkmonitor-api.txt
@@ -118,7 +118,7 @@ uint16 PrimaryScramblingCode [optional, umts]
 
 	Contains the scrambling code.  Valid range of values is 0-512.
 
-byte TimingAdvance [optional, gsm]
+byte TimingAdvance [optional, gsm, lte]
 
 	Contains the Timing Advance.  Valid range of values is 0-219.
 
@@ -165,3 +165,17 @@ byte ChannelQualityIndicator [optional, lte]
 
 	Contains Channel Quality Indicator.  Refer to Channel Quality
 	Indicator definition in 36.213, Section 7.2.3 for more details.
+
+uint16 PhysicalCellId [optional, lte]
+
+    Contains Physical Cell Id.  Valid range of values is 0-503.
+
+uint16 TrackingAreaCode [optional, lte]
+
+	Contains Tracking Area Code.  Valid range of values is 0-65535.
+
+int32 SingalToNoiseRatio [optional, lte]
+
+    Contains Signal To Noise Ratio.  Valid range of values is -200 to +300.
+    The value is in 0.1 dB units. (-200 = -20.0 dB, +300 = 30dB)
+    Refer to <snr> in 36.101, Section 8.1.1 for more details.
diff --git a/doc/radio-settings-api.txt b/doc/radio-settings-api.txt
index 9663b07..ad4a14d 100644
--- a/doc/radio-settings-api.txt
+++ b/doc/radio-settings-api.txt
@@ -50,6 +50,9 @@ Properties	string TechnologyPreference [readwrite]
 				"lte,umts"	Dual mode operation with LTE
 					and UMTS radio access with preference
 					for LTE.
+				"lte,gsm"	Dual mode operation with LTE
+					and GSM radio access with preference
+					for LTE.
 
 		array{string} AvailableTechnologies [readonly, optional]
 
diff --git a/doc/sim-api.txt b/doc/sim-api.txt
index b1428b1..419d8ec 100644
--- a/doc/sim-api.txt
+++ b/doc/sim-api.txt
@@ -79,6 +79,16 @@ Methods		dict GetProperties()
 					 [service].Error.InvalidArguments
 					 [service].Error.Failed
 
+		void SetProperty(string property, variant value)
+
+			Sets the given property value to that specified in
+			call parameter.
+
+			Possible Errors: [service].Error.InProgress
+					 [service].Error.NotImplemented
+					 [service].Error.InvalidArguments
+					 [service].Error.Failed
+
 Signals		PropertyChanged(string name, variant value)
 
 			This signal indicates a changed value of the given
diff --git a/drivers/atmodem/atutil.c b/drivers/atmodem/atutil.c
index bbdb01c..e4b6340 100644
--- a/drivers/atmodem/atutil.c
+++ b/drivers/atmodem/atutil.c
@@ -766,6 +766,7 @@ GAtChat *at_util_open_device(struct ofono_modem *modem, const char *key,
 			g_hash_table_insert(options, tty_option, value);
 			tty_option = (gpointer) va_arg(args, const char *);
 		}
+		va_end(args);
 	}
 
 	channel = g_at_tty_open(device, options);
diff --git a/drivers/atmodem/gprs.c b/drivers/atmodem/gprs.c
index b637f73..c208f6e 100644
--- a/drivers/atmodem/gprs.c
+++ b/drivers/atmodem/gprs.c
@@ -45,6 +45,7 @@
 #define MAX_CONTEXTS 255
 
 static const char *cgreg_prefix[] = { "+CGREG:", NULL };
+static const char *cgerep_prefix[] = { "+CGEREP:", NULL };
 static const char *cgdcont_prefix[] = { "+CGDCONT:", NULL };
 static const char *cgact_prefix[] = { "+CGACT:", NULL };
 static const char *none_prefix[] = { NULL };
@@ -624,6 +625,9 @@ static void gprs_initialized(gboolean ok, GAtResult *result, gpointer user_data)
 		g_at_chat_send(gd->chat, "AT#PSNT=1", none_prefix,
 						NULL, NULL, NULL);
 		break;
+	case OFONO_VENDOR_QUECTEL_EC2X:
+	case OFONO_VENDOR_QUECTEL_SERIAL:
+		break;
 	default:
 		g_at_chat_register(gd->chat, "+CPSB:", cpsb_notify,
 						FALSE, gprs, NULL);
@@ -645,6 +649,65 @@ static void gprs_initialized(gboolean ok, GAtResult *result, gpointer user_data)
 	ofono_gprs_register(gprs);
 }
 
+static void at_cgerep_test_cb(gboolean ok, GAtResult *result,
+				gpointer user_data)
+{
+	struct ofono_gprs *gprs = user_data;
+	struct gprs_data *gd = ofono_gprs_get_data(gprs);
+	GAtResultIter iter;
+	int min, max, arg1 = 0, arg2 = 0;
+	gboolean two_arguments = true;
+	char buf[20];
+
+	if (!ok) {
+		ofono_error("Error querying AT+CGEREP=? Failing...");
+		ofono_gprs_remove(gprs);
+		return;
+	}
+
+	g_at_result_iter_init(&iter, result);
+
+	g_at_result_iter_next(&iter, "+CGEREP:");
+
+	if (!g_at_result_iter_open_list(&iter)) {
+		ofono_error("Malformed reply from AT+CGEREP=? Failing...");
+		ofono_gprs_remove(gprs);
+		return;
+	}
+
+	while (g_at_result_iter_next_range(&iter, &min, &max)) {
+		if ((min <= 1) && (max >= 1))
+			arg1 = 1;
+
+		if ((min <= 2) && (max >= 2))
+			arg1 = 2;
+	}
+
+	if (!g_at_result_iter_close_list(&iter))
+		goto out;
+
+	if (!g_at_result_iter_open_list(&iter)) {
+		two_arguments = false;
+		goto out;
+	}
+
+	while (g_at_result_iter_next_range(&iter, &min, &max)) {
+		if ((min <= 1) && (max >= 1))
+			arg2 = 1;
+	}
+
+	g_at_result_iter_close_list(&iter);
+
+out:
+	if (two_arguments)
+		sprintf(buf, "AT+CGEREP=%u,%u", arg1, arg2);
+	else
+		sprintf(buf, "AT+CGEREP=%u", arg1);
+
+	g_at_chat_send(gd->chat, buf, none_prefix, gprs_initialized, gprs,
+		NULL);
+}
+
 static void at_cgreg_test_cb(gboolean ok, GAtResult *result,
 				gpointer user_data)
 {
@@ -699,8 +762,8 @@ retry:
 			gprs_initialized, gprs, NULL);
 		break;
 	default:
-		g_at_chat_send(gd->chat, "AT+CGEREP=2,1", none_prefix,
-			gprs_initialized, gprs, NULL);
+		g_at_chat_send(gd->chat, "AT+CGEREP=?", cgerep_prefix,
+			at_cgerep_test_cb, gprs, NULL);
 		break;
 	}
 
diff --git a/drivers/atmodem/network-registration.c b/drivers/atmodem/network-registration.c
index cc702c2..c1309f6 100644
--- a/drivers/atmodem/network-registration.c
+++ b/drivers/atmodem/network-registration.c
@@ -780,15 +780,26 @@ static void ifx_xciev_notify(GAtResult *result, gpointer user_data)
 	 */
 }
 
-static void ifx_xcsq_notify(GAtResult *result, gpointer user_data)
+static void ifx_quec_csq_notify(GAtResult *result, gpointer user_data)
 {
 	struct ofono_netreg *netreg = user_data;
+	struct at_netreg_data *nd = ofono_netreg_get_data(netreg);
 	int rssi, ber, strength;
 	GAtResultIter iter;
+	const char *prefix;
 
 	g_at_result_iter_init(&iter, result);
 
-	if (!g_at_result_iter_next(&iter, "+XCSQ:"))
+	switch (nd->vendor) {
+	case  OFONO_VENDOR_QUECTEL_SERIAL:
+		prefix = "+CSQN:";
+		break;
+	default:
+		prefix = "+XCSQ:";
+		break;
+	}
+
+	if (!g_at_result_iter_next(&iter, prefix))
 		return;
 
 	if (!g_at_result_iter_next_number(&iter, &rssi))
@@ -957,6 +968,66 @@ static void tlts_notify(GAtResult *result, gpointer user_data)
 	ofono_netreg_time_notify(netreg, &nd->time);
 }
 
+static void quectel_qind_notify(GAtResult *result, gpointer user_data)
+{
+	struct ofono_netreg *netreg = user_data;
+	struct at_netreg_data *nd = ofono_netreg_get_data(netreg);
+	int rssi, ber, strength;
+	const char *str;
+	GAtResultIter iter;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+QIND:"))
+		return;
+
+	if (!g_at_result_iter_next_string(&iter, &str))
+		return;
+
+	if (g_str_equal("csq", str)) {
+		if (!g_at_result_iter_next_number(&iter, &rssi))
+			return;
+
+		if (!g_at_result_iter_next_number(&iter, &ber))
+			return;
+
+		DBG("rssi %d ber %d", rssi, ber);
+
+		if ((rssi == 99) || (rssi == 199))
+			strength = -1;
+		else if (rssi > 99) {
+			rssi -= 100;
+			strength = (rssi * 100) / 91;
+		} else
+			strength = (rssi * 100) / 31;
+
+		ofono_netreg_strength_notify(netreg, strength);
+		return;
+	}
+
+	if (g_str_equal("act", str)) {
+		nd->tech = -1;
+		if (!g_at_result_iter_next_string(&iter, &str))
+			return;
+
+		DBG("technology %s", str);
+		if (g_str_equal("GSM", str))
+			nd->tech = ACCESS_TECHNOLOGY_GSM;
+		else if (g_str_equal("EGPRS", str))
+			nd->tech = ACCESS_TECHNOLOGY_GSM_EGPRS;
+		else if (g_str_equal("WCDMA", str))
+			nd->tech = ACCESS_TECHNOLOGY_UTRAN;
+		else if (g_str_equal("HSDPA", str))
+			nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA;
+		else if (g_str_equal("HSUPA", str))
+			nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSUPA;
+		else if (g_str_equal("HSDPA&HSUPA", str))
+			nd->tech = ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA;
+		else if (g_str_equal("LTE", str))
+			nd->tech = ACCESS_TECHNOLOGY_EUTRAN;
+	}
+}
+
 static gboolean notify_time(gpointer user_data)
 {
 	struct ofono_netreg *netreg = user_data;
@@ -1967,7 +2038,7 @@ static void at_creg_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
 		/* Register for specific signal strength reports */
 		g_at_chat_register(nd->chat, "+XCIEV:", ifx_xciev_notify,
 						FALSE, netreg, NULL);
-		g_at_chat_register(nd->chat, "+XCSQ:", ifx_xcsq_notify,
+		g_at_chat_register(nd->chat, "+XCSQ:", ifx_quec_csq_notify,
 						FALSE, netreg, NULL);
 		g_at_chat_send(nd->chat, "AT+XCSQ=1", none_prefix,
 						NULL, NULL, NULL);
@@ -2047,6 +2118,24 @@ static void at_creg_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
 	case OFONO_VENDOR_SAMSUNG:
 		/* Signal strength reporting via CIND is not supported */
 		break;
+	case OFONO_VENDOR_QUECTEL_EC2X:
+		g_at_chat_register(nd->chat, "+QIND:",
+				quectel_qind_notify, FALSE, netreg, NULL);
+		/* Register for specific signal strength reports */
+		g_at_chat_send(nd->chat, "AT+QINDCFG=\"csq\",1", none_prefix,
+				NULL, NULL, NULL);
+
+		/* Register for network technology updates */
+		g_at_chat_send(nd->chat, "AT+QINDCFG=\"act\",1", none_prefix,
+				NULL, NULL, NULL);
+		break;
+	case OFONO_VENDOR_QUECTEL_SERIAL:
+		g_at_chat_register(nd->chat, "+CSQN:",
+				ifx_quec_csq_notify, FALSE, netreg, NULL);
+		/* Register for specific signal strength reports */
+		g_at_chat_send(nd->chat, "AT+QEXTUNSOL=\"SQ\",1", none_prefix,
+				NULL, NULL, NULL);
+		break;
 	default:
 		g_at_chat_send(nd->chat, "AT+CIND=?", cind_prefix,
 				cind_support_cb, netreg, NULL);
diff --git a/drivers/atmodem/sim.c b/drivers/atmodem/sim.c
index e750a13..50eda69 100644
--- a/drivers/atmodem/sim.c
+++ b/drivers/atmodem/sim.c
@@ -199,6 +199,7 @@ static void at_sim_read_info(struct ofono_sim *sim, int fileid,
 	case OFONO_VENDOR_SPEEDUP:
 	case OFONO_VENDOR_QUALCOMM_MSM:
 	case OFONO_VENDOR_SIMCOM:
+	case OFONO_VENDOR_DROID:
 		/* Maximum possible length */
 		len += sprintf(buf + len, ",0,0,255");
 		break;
@@ -1213,6 +1214,7 @@ static void at_pin_retries_query(struct ofono_sim *sim,
 			return;
 		break;
 	case OFONO_VENDOR_QUECTEL:
+	case OFONO_VENDOR_QUECTEL_EC2X:
 		if (g_at_chat_send(sd->chat, "AT+QPINC?", qpinc_prefix,
 					at_qpinc_cb, cbd, g_free) > 0)
 			return;
diff --git a/drivers/atmodem/sms.c b/drivers/atmodem/sms.c
index d502da7..963c22e 100644
--- a/drivers/atmodem/sms.c
+++ b/drivers/atmodem/sms.c
@@ -337,6 +337,7 @@ static inline void at_ack_delivery(struct ofono_sms *sms)
 	if (data->cnma_ack_pdu) {
 		switch (data->vendor) {
 		case OFONO_VENDOR_GEMALTO:
+		case OFONO_VENDOR_QUECTEL_EC2X:
 			snprintf(buf, sizeof(buf), "AT+CNMA=1");
 			break;
 		case OFONO_VENDOR_QUECTEL_SERIAL:
@@ -463,7 +464,8 @@ static void at_cmt_notify(GAtResult *result, gpointer user_data)
 	decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu);
 	ofono_sms_deliver_notify(sms, pdu, pdu_len, tpdu_len);
 
-	if (data->vendor != OFONO_VENDOR_SIMCOM)
+	if (data->vendor != OFONO_VENDOR_SIMCOM &&
+			data->vendor != OFONO_VENDOR_DROID)
 		at_ack_delivery(sms);
 	return;
 
@@ -845,6 +847,8 @@ static gboolean build_cnmi_string(char *buf, int *cnmi_opts,
 	case OFONO_VENDOR_ZTE:
 	case OFONO_VENDOR_SIMCOM:
 	case OFONO_VENDOR_QUECTEL:
+	case OFONO_VENDOR_QUECTEL_EC2X:
+	case OFONO_VENDOR_DROID:
 		/* MSM devices advertise support for mode 2, but return an
 		 * error if we attempt to actually use it. */
 		mode = "1";
@@ -858,9 +862,15 @@ static gboolean build_cnmi_string(char *buf, int *cnmi_opts,
 	if (!append_cnmi_element(buf, &len, cnmi_opts[0], mode, FALSE))
 		return FALSE;
 
+	mode = "21";
+	if (!data->cnma_enabled)
+		mode = "1";
+
+	if (data->vendor == OFONO_VENDOR_DROID)
+		mode = "2";
+
 	/* Prefer to deliver SMS via +CMT if CNMA is supported */
-	if (!append_cnmi_element(buf, &len, cnmi_opts[1],
-					data->cnma_enabled ? "21" : "1", FALSE))
+	if (!append_cnmi_element(buf, &len, cnmi_opts[1], mode, FALSE))
 		return FALSE;
 
 	switch (data->vendor) {
@@ -1290,6 +1300,8 @@ static void at_csms_query_cb(gboolean ok, GAtResult *result,
 		goto out;
 
 	switch (data->vendor) {
+	case OFONO_VENDOR_DROID:
+		break;
 	case OFONO_VENDOR_QUECTEL_SERIAL:
 		g_at_result_iter_next_number(&iter, &status_min);
 		g_at_result_iter_next_number(&iter, &status_max);
diff --git a/drivers/atmodem/vendor.h b/drivers/atmodem/vendor.h
index d839d1e..82284e4 100644
--- a/drivers/atmodem/vendor.h
+++ b/drivers/atmodem/vendor.h
@@ -27,6 +27,7 @@ enum ofono_vendor {
 	OFONO_VENDOR_MBM,
 	OFONO_VENDOR_GOBI,
 	OFONO_VENDOR_QUALCOMM_MSM,
+	OFONO_VENDOR_DROID,
 	OFONO_VENDOR_OPTION_HSO,
 	OFONO_VENDOR_ZTE,
 	OFONO_VENDOR_HUAWEI,
@@ -44,6 +45,7 @@ enum ofono_vendor {
 	OFONO_VENDOR_WAVECOM_Q2XXX,
 	OFONO_VENDOR_ALCATEL,
 	OFONO_VENDOR_QUECTEL,
+	OFONO_VENDOR_QUECTEL_EC2X,
 	OFONO_VENDOR_QUECTEL_SERIAL,
 	OFONO_VENDOR_UBLOX,
 	OFONO_VENDOR_XMM,
diff --git a/drivers/atmodem/voicecall.c b/drivers/atmodem/voicecall.c
index 7ab6567..afd128f 100644
--- a/drivers/atmodem/voicecall.c
+++ b/drivers/atmodem/voicecall.c
@@ -161,6 +161,11 @@ static void clcc_poll_cb(gboolean ok, GAtResult *result, gpointer user_data)
 			goto poll_again;
 		}
 
+		if (vd->vendor == OFONO_VENDOR_DROID) {
+			poll_again = TRUE;
+			goto poll_again;
+		}
+
 		ofono_error("We are polling CLCC and received an error");
 		ofono_error("All bets are off for call management");
 		return;
@@ -1113,7 +1118,10 @@ static int at_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
 	g_at_chat_send(vd->chat, "AT+CRC=1", NULL, NULL, NULL, NULL);
 	g_at_chat_send(vd->chat, "AT+CLIP=1", NULL, NULL, NULL, NULL);
 	g_at_chat_send(vd->chat, "AT+CDIP=1", NULL, NULL, NULL, NULL);
-	g_at_chat_send(vd->chat, "AT+CNAP=1", NULL, NULL, NULL, NULL);
+
+	if (vd->vendor != OFONO_VENDOR_QUECTEL &&
+			vd->vendor != OFONO_VENDOR_QUECTEL_EC2X)
+		g_at_chat_send(vd->chat, "AT+CNAP=1", NULL, NULL, NULL, NULL);
 
 	switch (vd->vendor) {
 	case OFONO_VENDOR_QUALCOMM_MSM:
diff --git a/drivers/gemaltomodem/gemaltomodem.c b/drivers/gemaltomodem/gemaltomodem.c
index 4818ac6..bcf37e5 100644
--- a/drivers/gemaltomodem/gemaltomodem.c
+++ b/drivers/gemaltomodem/gemaltomodem.c
@@ -36,15 +36,21 @@
 static int gemaltomodem_init(void)
 {
 	gemalto_location_reporting_init();
+	gemalto_radio_settings_init();
+	gemalto_gprs_context_init();
 	gemalto_voicecall_init();
+	gemalto_netmon_init();
 
 	return 0;
 }
 
 static void gemaltomodem_exit(void)
 {
-	gemalto_voicecall_exit();
 	gemalto_location_reporting_exit();
+	gemalto_radio_settings_exit();
+	gemalto_gprs_context_exit();
+	gemalto_voicecall_exit();
+	gemalto_netmon_exit();
 }
 
 OFONO_PLUGIN_DEFINE(gemaltomodem, "Gemalto modem driver", VERSION,
diff --git a/drivers/gemaltomodem/gprs-context.c b/drivers/gemaltomodem/gprs-context.c
new file mode 100644
index 0000000..c0837be
--- /dev/null
+++ b/drivers/gemaltomodem/gprs-context.c
@@ -0,0 +1,361 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2020 Sergey Matyukevich. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs-context.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+#include "gattty.h"
+
+#include "gemaltomodem.h"
+
+static const char *none_prefix[] = { NULL };
+
+struct gprs_context_data {
+	GAtChat *chat;
+	unsigned int active_context;
+	char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1];
+	char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1];
+	int auth_type;
+	enum ofono_gprs_proto proto;
+	ofono_gprs_context_cb_t cb;
+	void *cb_data;
+};
+
+static void set_gprs_context_interface(struct ofono_gprs_context *gc)
+{
+	struct ofono_modem *modem;
+	const char *interface;
+
+	modem = ofono_gprs_context_get_modem(gc);
+	interface = ofono_modem_get_string(modem, "NetworkInterface");
+	ofono_gprs_context_set_interface(gc, interface);
+
+	/* Use DHCP */
+	ofono_gprs_context_set_ipv4_address(gc, NULL, 0);
+}
+
+static void swwan_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	struct ofono_error error;
+
+	DBG("ok %d", ok);
+
+	if (!ok) {
+		ofono_error("Unable to activate context");
+		ofono_gprs_context_deactivated(gc, gcd->active_context);
+		gcd->active_context = 0;
+		decode_at_error(&error, g_at_result_final_response(result));
+		gcd->cb(&error, gcd->cb_data);
+		return;
+	}
+}
+
+static void sgauth_enable_cb(gboolean ok, GAtResult *result,
+			gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	struct ofono_error error;
+	char buf[64];
+
+	DBG("ok %d", ok);
+
+	if (!ok) {
+		gcd->active_context = 0;
+		decode_at_error(&error, g_at_result_final_response(result));
+		gcd->cb(&error, gcd->cb_data);
+		return;
+	}
+
+	snprintf(buf, sizeof(buf), "AT^SWWAN=1,%u", gcd->active_context);
+
+	if (g_at_chat_send(gcd->chat, buf, none_prefix, swwan_cb, gc, NULL)) {
+		set_gprs_context_interface(gc);
+
+		CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+		return;
+	}
+
+	CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
+}
+
+static void cgdcont_enable_cb(gboolean ok, GAtResult *result,
+				gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	struct ofono_error error;
+	char buf[384];
+
+	DBG("ok %d", ok);
+
+	if (!ok) {
+		gcd->active_context = 0;
+		decode_at_error(&error, g_at_result_final_response(result));
+		gcd->cb(&error, gcd->cb_data);
+		return;
+	}
+
+	if (gcd->username[0] && gcd->password[0])
+		sprintf(buf, "AT^SGAUTH=%u,%u,\"%s\",\"%s\"",
+			gcd->active_context, gcd->auth_type,
+			gcd->username, gcd->password);
+	else
+		sprintf(buf, "AT^SGAUTH=%u,%u,\"\",\"\"",
+			gcd->active_context, gcd->auth_type);
+
+	if (g_at_chat_send(gcd->chat, buf, none_prefix,
+				sgauth_enable_cb, gc, NULL) > 0)
+		return;
+
+	CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
+}
+
+static void gemalto_gprs_activate_primary(struct ofono_gprs_context *gc,
+				const struct ofono_gprs_primary_context *ctx,
+				ofono_gprs_context_cb_t cb, void *data)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	char buf[OFONO_GPRS_MAX_APN_LENGTH + 128];
+	int len = 0;
+
+	DBG("cid %u", ctx->cid);
+
+	gcd->active_context = ctx->cid;
+	gcd->cb_data = data;
+	gcd->cb = cb;
+
+	memcpy(gcd->username, ctx->username, sizeof(ctx->username));
+	memcpy(gcd->password, ctx->password, sizeof(ctx->password));
+	gcd->proto = ctx->proto;
+
+	switch (ctx->auth_method) {
+	case OFONO_GPRS_AUTH_METHOD_PAP:
+		gcd->auth_type = 1;
+		break;
+	case OFONO_GPRS_AUTH_METHOD_CHAP:
+		gcd->auth_type = 2;
+		break;
+	case OFONO_GPRS_AUTH_METHOD_NONE:
+	default:
+		gcd->auth_type = 0;
+		break;
+	}
+
+	switch (ctx->proto) {
+	case OFONO_GPRS_PROTO_IP:
+		len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"",
+				ctx->cid);
+		break;
+	case OFONO_GPRS_PROTO_IPV6:
+		len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV6\"",
+				ctx->cid);
+		break;
+	case OFONO_GPRS_PROTO_IPV4V6:
+		len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV4V6\"",
+				ctx->cid);
+		break;
+	}
+
+	if (ctx->apn)
+		snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", ctx->apn);
+
+	if (g_at_chat_send(gcd->chat, buf, none_prefix,
+				cgdcont_enable_cb, gc, NULL))
+		return;
+
+	CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void deactivate_cb(gboolean ok, GAtResult *result,
+		gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+	DBG("ok %d", ok);
+
+	gcd->active_context = 0;
+
+	if (!ok) {
+		CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
+		return;
+	}
+
+	CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+}
+
+static void gemalto_gprs_deactivate_primary(struct ofono_gprs_context *gc,
+						unsigned int cid,
+						ofono_gprs_context_cb_t cb,
+						void *data)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	char buf[64];
+
+	DBG("cid %u", cid);
+
+	gcd->cb = cb;
+	gcd->cb_data = data;
+
+	snprintf(buf, sizeof(buf), "AT^SWWAN=0,%u", gcd->active_context);
+
+	if (g_at_chat_send(gcd->chat, buf, none_prefix,
+				deactivate_cb, gc, NULL))
+		return;
+
+	CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void gemalto_gprs_read_settings(struct ofono_gprs_context *gc,
+					unsigned int cid,
+					ofono_gprs_context_cb_t cb, void *data)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	char buf[64];
+
+	DBG("cid %u", cid);
+
+	gcd->active_context = cid;
+	gcd->cb = cb;
+	gcd->cb_data = data;
+
+	/*
+	 * AT^SWWAN command activates PDP context unless it has been already
+	 * activated automatically, and then starts DHCP server in the ME.
+	 * So AT^SWWAN command should be run in both cases:
+	 * - when activate context and then obtain IP address from the ME
+	 * - when obtain IP address from the automatically activated context
+	 *
+	 * Note that the ME waits until DHCP negotiation has finished before
+	 * sending the "OK" or "ERROR" result code. So success is reported
+	 * to the core before AT^SWWAN response.
+	 */
+	snprintf(buf, sizeof(buf), "AT^SWWAN=1,%u", gcd->active_context);
+
+	if (g_at_chat_send(gcd->chat, buf, none_prefix, swwan_cb, gc, NULL)) {
+		set_gprs_context_interface(gc);
+
+		CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
+		return;
+	}
+
+	CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
+}
+
+static void cgev_notify(GAtResult *result, gpointer user_data)
+{
+	struct ofono_gprs_context *gc = user_data;
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	GAtResultIter iter;
+	const char *event;
+	gint cid;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+CGEV:"))
+		return;
+
+	if (!g_at_result_iter_next_unquoted_string(&iter, &event))
+		return;
+
+	if (g_str_has_prefix(event, "NW PDN DEACT"))
+		sscanf(event, "%*s %*s %*s %u", &cid);
+	else if (g_str_has_prefix(event, "ME PDN DEACT"))
+		sscanf(event, "%*s %*s %*s %u", &cid);
+	else if (g_str_has_prefix(event, "NW DEACT"))
+		sscanf(event, "%*s %*s %u", &cid);
+	else
+		return;
+
+	DBG("cid %d, active cid: %d", cid, gcd->active_context);
+
+	if ((unsigned int) cid != gcd->active_context)
+		return;
+
+	ofono_gprs_context_deactivated(gc, gcd->active_context);
+	gcd->active_context = 0;
+}
+
+static int gemalto_gprs_context_probe(struct ofono_gprs_context *gc,
+					unsigned int vendor, void *data)
+{
+	GAtChat *chat = data;
+	struct gprs_context_data *gcd;
+
+	DBG("");
+
+	gcd = g_new0(struct gprs_context_data, 1);
+
+	gcd->chat = g_at_chat_clone(chat);
+
+	ofono_gprs_context_set_data(gc, gcd);
+	g_at_chat_register(chat, "+CGEV:", cgev_notify, FALSE, gc, NULL);
+
+	return 0;
+}
+
+static void gemalto_gprs_context_remove(struct ofono_gprs_context *gc)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+	DBG("");
+
+	ofono_gprs_context_set_data(gc, NULL);
+	g_at_chat_unref(gcd->chat);
+	g_free(gcd);
+}
+
+static const struct ofono_gprs_context_driver driver = {
+	.name			= "gemaltomodem",
+	.probe			= gemalto_gprs_context_probe,
+	.remove			= gemalto_gprs_context_remove,
+	.activate_primary	= gemalto_gprs_activate_primary,
+	.deactivate_primary	= gemalto_gprs_deactivate_primary,
+	.read_settings		= gemalto_gprs_read_settings,
+};
+
+void gemalto_gprs_context_init(void)
+{
+	ofono_gprs_context_driver_register(&driver);
+}
+
+void gemalto_gprs_context_exit(void)
+{
+	ofono_gprs_context_driver_unregister(&driver);
+}
diff --git a/drivers/gemaltomodem/netmon.c b/drivers/gemaltomodem/netmon.c
new file mode 100644
index 0000000..d7959da
--- /dev/null
+++ b/drivers/gemaltomodem/netmon.c
@@ -0,0 +1,651 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/netreg.h>
+#include <ofono/netmon.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "common.h"
+#include "gemaltomodem.h"
+#include "drivers/atmodem/vendor.h"
+
+static const char *smoni_prefix[] = { "^SMONI:", NULL };
+static const char *csq_prefix[] = { "+CSQ:", NULL };
+
+struct netmon_driver_data {
+	GAtChat *chat;
+};
+
+struct req_cb_data {
+	gint ref_count; /* Ref count */
+
+	struct ofono_netmon *netmon;
+	ofono_netmon_cb_t cb;
+	void *data;
+
+	struct ofono_network_operator op;
+
+	int rssi;	/* CSQ: received signal strength indicator (RSSI) */
+
+	union {
+		struct {
+			int arfcn;	/* SMONI: Absolute Radio Frequency Channel Number */
+			int bcch;	/* SMONI: Receiving level of the BCCH carrier in dBm */
+			int lac;	/* SMONI: Location Area Code */
+			int ci;		/* SMONI: Cell ID */
+		} gsm;
+		struct {
+			int uarfcn;	/* SMONI: UTRAN Absolute Radio Frequency Channel Number */
+			int psc;	/* SMONI: Primary Scrambling Code */
+			int ecno;	/* SMONI: Carrier to noise ratio in dB */
+			int rscp;	/* SMONI: Received Signal Code Power in dBm */
+			int lac;	/* SMONI: Location Area Code */
+			int ci;		/* SMONI: Cell ID */
+		} umts;
+		struct {
+			int euarfcn;	/* SMONI: E-UTRA Absolute Radio Frequency Channel Number */
+			int rsrp;	/* SMONI: Reference Signal Received Power */
+			int rsrq;	/* SMONI: Reference Signal Received Quality */
+		} lte;
+	} t;
+};
+
+static inline struct req_cb_data *req_cb_data_new0(void *cb, void *data,
+							void *user)
+{
+	struct req_cb_data *ret = g_new0(struct req_cb_data, 1);
+
+	ret->ref_count = 1;
+	ret->netmon = user;
+	ret->data = data;
+	ret->cb = cb;
+
+	return ret;
+}
+
+static inline struct req_cb_data *req_cb_data_ref(struct req_cb_data *cbd)
+{
+	if (cbd == NULL)
+		return NULL;
+
+	g_atomic_int_inc(&cbd->ref_count);
+
+	return cbd;
+}
+
+static void req_cb_data_unref(gpointer user_data)
+{
+	struct req_cb_data *cbd = user_data;
+	gboolean is_zero;
+
+	if (cbd == NULL)
+		return;
+
+	is_zero = g_atomic_int_dec_and_test(&cbd->ref_count);
+
+	if (is_zero == TRUE)
+		g_free(cbd);
+}
+
+static gboolean gemalto_delayed_register(gpointer user_data)
+{
+	struct ofono_netmon *netmon = user_data;
+
+	ofono_netmon_register(netmon);
+
+	return FALSE;
+}
+
+static int gemalto_ecno_scale(int value)
+{
+	if (value < -24)
+		return 0;
+
+	if (value > 0)
+		return 49;
+
+	return 49 * (value + 24) / 24;
+}
+
+static int gemalto_rscp_scale(int value)
+{
+	if (value < -120)
+		return 0;
+
+	if (value > -24)
+		return 96;
+
+	return value + 120;
+}
+
+static int gemalto_rsrp_scale(int value)
+{
+	if (value < -140)
+		return 0;
+
+	if (value > -43)
+		return 97;
+
+	return value + 140;
+}
+
+static int gemalto_rsrq_scale(int value)
+{
+	if (2 * value < -39)
+		return 0;
+
+	if (2 * value > -5)
+		return 34;
+
+	return 2 * value + 39;
+}
+
+static int gemalto_parse_smoni_gsm(GAtResultIter *iter,
+					struct req_cb_data *cbd)
+{
+	/*
+	 * ME is camping on a GSM (2G) cell:
+	 * ^SMONI: ACT,ARFCN,BCCH,MCC,MNC,LAC,cell,C1,C2,NCC,BCC,GPRS,Conn_state
+	 * ^SMONI: 2G,71,-61,262,02,0143,83BA,33,33,3,6,G,NOCONN
+	 *
+	 * ME is searching and could not (yet) find a suitable GSM (2G) cell:
+	 * ^SMONI: ACT,ARFCN,BCCH,MCC,MNC,LAC,cell,C1,C2,NCC,BCC,GPRS,ARFCN,TS,timAdv,dBm,Q,ChMod
+	 * ^SMONI: 2G,SEARCH,SEARCH
+	 *
+	 * ME is camping on a GSM cell but not registered to the network (only emergency call allowed):
+	 * ^SMONI: ACT,ARFCN,BCCH,MCC,MNC,LAC,cell,C1,C2,NCC,BCC,GPRS,PWR,RXLev,ARFCN,TS,timAdv,dBm,Q,ChMod
+	 * ^SMONI: 2G,673,-89,262,07,4EED,A500,16,16,7,4,G,5,-107,LIMSRV
+	 *
+	 * ME has a dedicated channel (for example call in progress):
+	 * ^SMONI: ACT,ARFCN,BCCH,MCC,MNC,LAC,cell,C1,C2,NCC,BCC,GPRS,ARFCN,TS,timAdv,dBm,Q,ChMod
+	 * ^SMONI: 2G,673,-80,262,07,4EED,A500,35,35,7,4,G,643,4,0,-80,0,S_FR
+	 */
+
+	enum smoni_gsm_field {
+		SMONI_GSM_ARFCN,
+		SMONI_GSM_BCCH,
+		SMONI_GSM_MCC,
+		SMONI_GSM_MNC,
+		SMONI_GSM_LAC,
+		SMONI_GSM_CI,
+		SMONI_GSM_MAX,
+	};
+
+	const char *str;
+	int number;
+	int idx;
+
+	cbd->t.gsm.arfcn = -1;
+	cbd->t.gsm.bcch = -1;
+	cbd->t.gsm.lac = -1;
+	cbd->t.gsm.ci = -1;
+
+	for (idx = 0; idx < SMONI_GSM_MAX; idx++) {
+		switch (idx) {
+		case SMONI_GSM_ARFCN:
+			if (g_at_result_iter_next_number(iter, &number))
+				cbd->t.gsm.arfcn = number;
+			break;
+		case SMONI_GSM_BCCH:
+			if (g_at_result_iter_next_unquoted_string(iter, &str)) {
+				if (sscanf(str, "%d", &number) == 1)
+					cbd->t.gsm.bcch = number;
+			}
+			break;
+		case SMONI_GSM_MCC:
+			if (g_at_result_iter_next_number(iter, &number))
+				snprintf(cbd->op.mcc, 4, "%d", number);
+			break;
+		case SMONI_GSM_MNC:
+			if (g_at_result_iter_next_number(iter, &number))
+				snprintf(cbd->op.mnc, 4, "%d", number);
+			break;
+		case SMONI_GSM_LAC:
+			if (g_at_result_iter_next_unquoted_string(iter, &str)) {
+				if (sscanf(str, "%x", &number) == 1)
+					cbd->t.gsm.lac = number;
+			}
+			break;
+		case SMONI_GSM_CI:
+			if (g_at_result_iter_next_unquoted_string(iter, &str)) {
+				if (sscanf(str, "%x", &number) == 1)
+					cbd->t.gsm.ci = number;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	DBG(" %-15s %s", "GSM.MCC", cbd->op.mcc);
+	DBG(" %-15s %s", "GSM.MNC", cbd->op.mnc);
+	DBG(" %-15s %d", "GSM.ARFCN", cbd->t.gsm.arfcn);
+	DBG(" %-15s %d", "GSM.BCCH", cbd->t.gsm.bcch);
+	DBG(" %-15s %d", "GSM.LAC", cbd->t.gsm.lac);
+	DBG(" %-15s %d", "GSM.CELL", cbd->t.gsm.ci);
+
+	return 0;
+}
+
+static int gemalto_parse_smoni_umts(GAtResultIter *iter,
+					struct req_cb_data *cbd)
+{
+	/*
+	 * ME is camping on a UMTS (3G) cell:
+	 * ^SMONI: ACT,UARFCN,PSC,EC/n0,RSCP,MCC,MNC,LAC,cell,SQual,SRxLev,,Conn_state
+	 * ^SMONI: 3G,10564,296,-7.5,-79,262,02,0143,00228FF,-92,-78,NOCONN
+	 *
+	 * ME is searching and could not (yet) find a suitable UMTS (3G) cell:
+	 * ^SMONI: ACT,UARFCN,PSC,EC/n0,RSCP,MCC,MNC,LAC,cell,SQual,SRxLev,PhysCh, SF,Slot,EC/n0,RSCP,ComMod,HSUPA,HSDPA
+	 * ^SMONI: 3G,SEARCH,SEARCH
+	 *
+	 * ME is camping on a UMTS cell but not registered to the network (only emergency call allowed):
+	 * ^SMONI: ACT,UARFCN,PSC,EC/n0,RSCP,MCC,MNC,LAC,cell,SQual,SRxLev,PhysCh, SF,Slot,EC/n0,RSCP,ComMod,HSUPA,HSDPA
+	 * ^SMONI: 3G,10564,96,-7.5,-79,262,02,0143,00228FF,-92,-78,LIMSRV
+	 *
+	 * ME has a dedicated channel (for example call in progress):
+	 * ^SMONI: ACT,UARFCN,PSC,EC/n0,RSCP,MCC,MNC,LAC,cell,SQual,SRxLev,PhysCh, SF,Slot,EC/n0,RSCP,ComMod,HSUPA,HSDPA
+	 * ^SMONI: 3G,10737,131,-5,-93,260,01,7D3D,C80BC9A,--,--,----,---,-,-5,-93,0,01,06
+	 */
+
+	enum smoni_umts_field {
+		SMONI_UMTS_UARFCN,
+		SMONI_UMTS_PSC,
+		SMONI_UMTS_ECN0,
+		SMONI_UMTS_RSCP,
+		SMONI_UMTS_MCC,
+		SMONI_UMTS_MNC,
+		SMONI_UMTS_LAC,
+		SMONI_UMTS_CI,
+		SMONI_UMTS_MAX,
+	};
+
+	const char *str;
+	float fnumber;
+	int number;
+	int idx;
+
+	cbd->t.umts.uarfcn = -1;
+	cbd->t.umts.psc = -1;
+	cbd->t.umts.ecno = -1;
+	cbd->t.umts.rscp = -1;
+	cbd->t.umts.lac = -1;
+	cbd->t.umts.ci = -1;
+
+	for (idx = 0; idx < SMONI_UMTS_MAX; idx++) {
+		switch (idx) {
+		case SMONI_UMTS_UARFCN:
+			if (g_at_result_iter_next_number(iter, &number))
+				cbd->t.umts.uarfcn = number;
+			break;
+		case SMONI_UMTS_PSC:
+			if (g_at_result_iter_next_number(iter, &number))
+				cbd->t.umts.psc = number;
+			break;
+		case SMONI_UMTS_ECN0:
+			if (g_at_result_iter_next_unquoted_string(iter, &str)) {
+				if (sscanf(str, "%f", &fnumber) == 1)
+					cbd->t.umts.ecno =
+						gemalto_ecno_scale((int)fnumber);
+			}
+			break;
+		case SMONI_UMTS_RSCP:
+			if (g_at_result_iter_next_unquoted_string(iter, &str)) {
+				if (sscanf(str, "%d", &number) == 1)
+					cbd->t.umts.rscp =
+						gemalto_rscp_scale(number);
+			}
+			break;
+		case SMONI_UMTS_MCC:
+			if (g_at_result_iter_next_number(iter, &number))
+				snprintf(cbd->op.mcc, 4, "%d", number);
+			break;
+		case SMONI_UMTS_MNC:
+			if (g_at_result_iter_next_number(iter, &number))
+				snprintf(cbd->op.mnc, 4, "%d", number);
+			break;
+		case SMONI_UMTS_LAC:
+			if (g_at_result_iter_next_unquoted_string(iter, &str)) {
+				if (sscanf(str, "%x", &number) == 1)
+					cbd->t.umts.lac = number;
+			}
+			break;
+		case SMONI_UMTS_CI:
+			if (g_at_result_iter_next_unquoted_string(iter, &str)) {
+				if (sscanf(str, "%x", &number) == 1)
+					cbd->t.umts.ci = number;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	DBG(" %-15s %s", "UMTS.MCC", cbd->op.mcc);
+	DBG(" %-15s %s", "UMTS.MNC", cbd->op.mnc);
+	DBG(" %-15s %d", "UMTS.UARFCN", cbd->t.umts.uarfcn);
+	DBG(" %-15s %d", "UMTS.PSC", cbd->t.umts.psc);
+	DBG(" %-15s %d", "UMTS.ECN0", cbd->t.umts.ecno);
+	DBG(" %-15s %d", "UMTS.RSCP", cbd->t.umts.rscp);
+	DBG(" %-15s %d", "UMTS.LAC", cbd->t.umts.lac);
+	DBG(" %-15s %d", "UMTS.CELL", cbd->t.umts.ci);
+
+	return 0;
+}
+
+static int gemalto_parse_smoni_lte(GAtResultIter *iter,
+					struct req_cb_data *cbd)
+{
+	/*
+	 * ME is camping on a LTE (4G) cell:
+	 * ^SMONI: ACT,EARFCN,Band,DL bandwidth,UL bandwidth,Mode,MCC,MNC,TAC,Global Cell ID,Phys-ical Cell ID,Srxlev,RSRP,RSRQ,Conn_state
+	 * ^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,33,-94,-7,NOCONN
+	 *
+	 * ME is searching and could not (yet) find a suitable LTE (4G) cell:
+	 * ^SMONI: ACT,EARFCN,Band,DL bandwidth,UL bandwidth,Mode,MCC,MNC,TAC,Global Cell ID,Phys-ical Cell ID,Srxlev,RSRP,RSRQ,Conn_state
+	 * ^SMONI: 4G,SEARCH
+	 *
+	 * ME is camping on a LTE (4G) cell but not registered to the network (only emergency call allowed):
+	 * ^SMONI: ACT,EARFCN,Band,DL bandwidth,UL bandwidth,Mode,MCC,MNC,TAC,Global Cell ID,Phys-ical Cell ID,Srxlev,RSRP,RSRQ,Conn_state
+	 * ^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,33,-94,-7,LIMSRV
+	 *
+	 * ME has a dedicated channel (for example call in progress):
+	 * ^SMONI: ACT,EARFCN,Band,DL bandwidth,UL bandwidth,Mode,MCC,MNC,TAC,Global Cell ID,Phys-ical Cell ID,TX_power,RSRP,RSRQ,Conn_state
+	 * ^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,90,-94,-7,CONN
+	 */
+
+	const char *str;
+	int number;
+
+	cbd->t.lte.euarfcn = -1;
+	cbd->t.lte.rsrp = -1;
+	cbd->t.lte.rsrq = -1;
+
+	if (g_at_result_iter_next_number(iter, &number))
+		cbd->t.lte.euarfcn = number;
+
+	g_at_result_iter_skip_next(iter);
+	g_at_result_iter_skip_next(iter);
+	g_at_result_iter_skip_next(iter);
+	g_at_result_iter_skip_next(iter);
+
+	if (g_at_result_iter_next_number(iter, &number))
+		snprintf(cbd->op.mcc, 4, "%d", number);
+
+	if (g_at_result_iter_next_number(iter, &number))
+		snprintf(cbd->op.mnc, 4, "%d", number);
+
+	g_at_result_iter_skip_next(iter);
+	g_at_result_iter_skip_next(iter);
+	g_at_result_iter_skip_next(iter);
+	g_at_result_iter_skip_next(iter);
+
+	if (g_at_result_iter_next_unquoted_string(iter, &str)) {
+		if (sscanf(str, "%d", &number) == 1)
+			cbd->t.lte.rsrp = gemalto_rsrp_scale(number);
+	}
+
+	if (g_at_result_iter_next_unquoted_string(iter, &str)) {
+		if (sscanf(str, "%d", &number) == 1)
+			cbd->t.lte.rsrq = gemalto_rsrq_scale(number);
+	}
+
+	DBG(" %-15s %s", "LTE.MCC", cbd->op.mcc);
+	DBG(" %-15s %s", "LTE.MNC", cbd->op.mnc);
+	DBG(" %-15s %d", "LTE.EUARFCN", cbd->t.lte.euarfcn);
+	DBG(" %-15s %d", "LTE.RSRP", cbd->t.lte.rsrp);
+	DBG(" %-15s %d", "LTE.RSRQ", cbd->t.lte.rsrq);
+
+	return 0;
+}
+
+static void gemalto_netmon_finish_success(struct req_cb_data *cbd)
+{
+	struct ofono_netmon *nm = cbd->netmon;
+
+	switch (cbd->op.tech) {
+	case OFONO_NETMON_CELL_TYPE_LTE:
+		ofono_netmon_serving_cell_notify(nm, cbd->op.tech,
+					OFONO_NETMON_INFO_MCC, cbd->op.mcc,
+					OFONO_NETMON_INFO_MNC, cbd->op.mnc,
+					OFONO_NETMON_INFO_RSSI, cbd->rssi,
+					OFONO_NETMON_INFO_EARFCN, cbd->t.lte.euarfcn,
+					OFONO_NETMON_INFO_RSRP, cbd->t.lte.rsrp,
+					OFONO_NETMON_INFO_RSRQ, cbd->t.lte.rsrq,
+					OFONO_NETMON_INFO_INVALID);
+		break;
+	case OFONO_NETMON_CELL_TYPE_UMTS:
+		ofono_netmon_serving_cell_notify(nm, cbd->op.tech,
+					OFONO_NETMON_INFO_MCC, cbd->op.mcc,
+					OFONO_NETMON_INFO_MNC, cbd->op.mnc,
+					OFONO_NETMON_INFO_RSSI, cbd->rssi,
+					OFONO_NETMON_INFO_ARFCN, cbd->t.umts.uarfcn,
+					OFONO_NETMON_INFO_PSC, cbd->t.umts.psc,
+					OFONO_NETMON_INFO_ECN0, cbd->t.umts.ecno,
+					OFONO_NETMON_INFO_RSCP, cbd->t.umts.rscp,
+					OFONO_NETMON_INFO_LAC, cbd->t.umts.lac,
+					OFONO_NETMON_INFO_CI, cbd->t.umts.ci,
+					OFONO_NETMON_INFO_INVALID);
+		break;
+	case OFONO_NETMON_CELL_TYPE_GSM:
+		ofono_netmon_serving_cell_notify(nm, cbd->op.tech,
+					OFONO_NETMON_INFO_MCC, cbd->op.mcc,
+					OFONO_NETMON_INFO_MNC, cbd->op.mnc,
+					OFONO_NETMON_INFO_RSSI, cbd->rssi,
+					OFONO_NETMON_INFO_ARFCN, cbd->t.gsm.arfcn,
+					OFONO_NETMON_INFO_LAC, cbd->t.gsm.lac,
+					OFONO_NETMON_INFO_CI, cbd->t.gsm.ci,
+					OFONO_NETMON_INFO_INVALID);
+		break;
+	default:
+		break;
+	}
+
+	CALLBACK_WITH_SUCCESS(cbd->cb, cbd->data);
+}
+
+static void csq_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct req_cb_data *cbd = user_data;
+	struct ofono_error error;
+	GAtResultIter iter;
+	int rssi;
+
+	DBG("ok %d", ok);
+
+	decode_at_error(&error, g_at_result_final_response(result));
+
+	if (!ok) {
+		CALLBACK_WITH_FAILURE(cbd->cb, cbd->data);
+		return;
+	}
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+CSQ: ")) {
+		cbd->rssi = -1;
+		goto out;
+	}
+
+	if (!g_at_result_iter_next_number(&iter, &rssi) || rssi == 99)
+		cbd->rssi = -1;
+	else
+		cbd->rssi = rssi;
+
+	DBG(" RSSI %d ", cbd->rssi);
+
+out:
+	gemalto_netmon_finish_success(cbd);
+}
+
+static void smoni_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct req_cb_data *cbd = user_data;
+	struct ofono_netmon *nm = cbd->netmon;
+	struct netmon_driver_data *nmd = ofono_netmon_get_data(nm);
+	struct ofono_error error;
+	const char *technology;
+	GAtResultIter iter;
+	int ret;
+
+	DBG("ok %d", ok);
+
+	decode_at_error(&error, g_at_result_final_response(result));
+
+	if (!ok) {
+		CALLBACK_WITH_FAILURE(cbd->cb, cbd->data);
+		return;
+	}
+
+	g_at_result_iter_init(&iter, result);
+
+	/* do not fail */
+
+	if (!g_at_result_iter_next(&iter, "^SMONI: ")) {
+		CALLBACK_WITH_SUCCESS(cbd->cb, cbd->data);
+		return;
+	}
+
+	if (!g_at_result_iter_next_unquoted_string(&iter, &technology)) {
+		DBG("^SMONI: failed to parse technology");
+		CALLBACK_WITH_SUCCESS(cbd->cb, cbd->data);
+		return;
+	}
+
+	if (strcmp(technology, "2G") == 0) {
+		cbd->op.tech = OFONO_NETMON_CELL_TYPE_GSM;
+	} else if (strcmp(technology, "3G") == 0) {
+		cbd->op.tech = OFONO_NETMON_CELL_TYPE_UMTS;
+	} else if (strcmp(technology, "4G") == 0) {
+		cbd->op.tech = OFONO_NETMON_CELL_TYPE_LTE;
+	} else {
+		/* fall-back to GSM by default */
+		DBG("^SMONI: unexpected technology: %s", technology);
+		cbd->op.tech = OFONO_NETMON_CELL_TYPE_GSM;
+	}
+
+	switch (cbd->op.tech) {
+	case OFONO_NETMON_CELL_TYPE_LTE:
+		ret = gemalto_parse_smoni_lte(&iter, cbd);
+		break;
+	case OFONO_NETMON_CELL_TYPE_UMTS:
+		ret = gemalto_parse_smoni_umts(&iter, cbd);
+		break;
+	case OFONO_NETMON_CELL_TYPE_GSM:
+		ret = gemalto_parse_smoni_gsm(&iter, cbd);
+		break;
+	default:
+		break;
+	}
+
+	if (ret) {
+		CALLBACK_WITH_SUCCESS(cbd->cb, cbd->data);
+		return;
+	}
+
+	cbd = req_cb_data_ref(cbd);
+	if (g_at_chat_send(nmd->chat, "AT+CSQ", csq_prefix,
+				csq_cb, cbd, req_cb_data_unref))
+		return;
+
+	req_cb_data_unref(cbd);
+	CALLBACK_WITH_FAILURE(cbd->cb, cbd->data);
+}
+
+static void gemalto_netmon_request_update(struct ofono_netmon *netmon,
+						ofono_netmon_cb_t cb,
+						void *data)
+{
+	struct netmon_driver_data *nmd = ofono_netmon_get_data(netmon);
+	struct req_cb_data *cbd;
+
+	DBG("gemalto netmon request update");
+
+	cbd = req_cb_data_new0(cb, data, netmon);
+
+	if (g_at_chat_send(nmd->chat, "AT^SMONI", smoni_prefix,
+				smoni_cb, cbd, req_cb_data_unref))
+		return;
+
+	req_cb_data_unref(cbd);
+	CALLBACK_WITH_FAILURE(cbd->cb, cbd->data);
+}
+
+static int gemalto_netmon_probe(struct ofono_netmon *netmon,
+					unsigned int vendor, void *user)
+{
+	struct netmon_driver_data *nmd = g_new0(struct netmon_driver_data, 1);
+	GAtChat *chat = user;
+
+	DBG("gemalto netmon probe");
+
+	nmd->chat = g_at_chat_clone(chat);
+
+	ofono_netmon_set_data(netmon, nmd);
+
+	g_idle_add(gemalto_delayed_register, netmon);
+
+	return 0;
+}
+
+static void gemalto_netmon_remove(struct ofono_netmon *netmon)
+{
+	struct netmon_driver_data *nmd = ofono_netmon_get_data(netmon);
+
+	DBG("gemalto netmon remove");
+
+	g_at_chat_unref(nmd->chat);
+
+	ofono_netmon_set_data(netmon, NULL);
+
+	g_free(nmd);
+}
+
+static const struct ofono_netmon_driver driver = {
+	.name			= "gemaltomodem",
+	.probe			= gemalto_netmon_probe,
+	.remove			= gemalto_netmon_remove,
+	.request_update		= gemalto_netmon_request_update,
+};
+
+void gemalto_netmon_init(void)
+{
+	ofono_netmon_driver_register(&driver);
+}
+
+void gemalto_netmon_exit(void)
+{
+	ofono_netmon_driver_unregister(&driver);
+}
diff --git a/drivers/gemaltomodem/radio-settings.c b/drivers/gemaltomodem/radio-settings.c
new file mode 100644
index 0000000..50764f8
--- /dev/null
+++ b/drivers/gemaltomodem/radio-settings.c
@@ -0,0 +1,264 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2017 Intel Corporation. All rights reserved.
+ *  Copyright (C) 2021 Sergey Matyukevich. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/radio-settings.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "gemaltomodem.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *sxrat_prefix[] = { "^SXRAT:", NULL };
+
+struct radio_settings_data {
+	GAtChat *chat;
+};
+
+static void sxrat_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
+	unsigned int mode;
+	struct ofono_error error;
+	int value, pref1, pref2;
+	GAtResultIter iter;
+
+	DBG("ok %d", ok);
+
+	decode_at_error(&error, g_at_result_final_response(result));
+
+	if (!ok) {
+		cb(&error, -1, cbd->data);
+		return;
+	}
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "^SXRAT:"))
+		goto error;
+
+	if (!g_at_result_iter_next_number(&iter, &value))
+		goto error;
+
+	g_at_result_iter_next_number_default(&iter, -1, &pref1);
+	g_at_result_iter_next_number_default(&iter, -1, &pref2);
+
+	DBG("mode %d pref1 %d pref2 %d", value, pref1, pref2);
+
+	switch (value) {
+	case 0:
+		mode = OFONO_RADIO_ACCESS_MODE_GSM;
+		break;
+	case 1:
+		mode = OFONO_RADIO_ACCESS_MODE_GSM |
+			OFONO_RADIO_ACCESS_MODE_UMTS;
+		break;
+	case 2:
+		mode = OFONO_RADIO_ACCESS_MODE_UMTS;
+		break;
+	case 3:
+		mode = OFONO_RADIO_ACCESS_MODE_LTE;
+		break;
+	case 4:
+		mode = OFONO_RADIO_ACCESS_MODE_UMTS |
+			OFONO_RADIO_ACCESS_MODE_LTE;
+		break;
+	case 5:
+		mode = OFONO_RADIO_ACCESS_MODE_GSM |
+			OFONO_RADIO_ACCESS_MODE_LTE;
+		break;
+	case 6:
+		mode = OFONO_RADIO_ACCESS_MODE_ANY;
+		break;
+	default:
+		CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+		return;
+	}
+
+	cb(&error, mode, cbd->data);
+
+	return;
+
+error:
+	CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void gemalto_query_rat_mode(struct ofono_radio_settings *rs,
+				ofono_radio_settings_rat_mode_query_cb_t cb,
+				void *data)
+{
+	struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+	struct cb_data *cbd = cb_data_new(cb, data);
+
+	DBG("");
+
+	if (g_at_chat_send(rsd->chat, "AT^SXRAT?", sxrat_prefix,
+				sxrat_query_cb, cbd, g_free) == 0) {
+		CALLBACK_WITH_FAILURE(cb, -1, data);
+		g_free(cbd);
+	}
+}
+
+static void sxrat_modify_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb;
+	struct ofono_error error;
+
+	DBG("ok %d", ok);
+
+	decode_at_error(&error, g_at_result_final_response(result));
+	cb(&error, cbd->data);
+}
+
+static void gemalto_set_rat_mode(struct ofono_radio_settings *rs,
+				unsigned int m,
+				ofono_radio_settings_rat_mode_set_cb_t cb,
+				void *data)
+{
+	struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+	struct cb_data *cbd = cb_data_new(cb, data);
+	int val= 6, p1 = 3, p2 = 2;
+	char buf[20];
+
+	DBG("mode %d", m);
+
+	switch (m) {
+	case OFONO_RADIO_ACCESS_MODE_ANY:
+		val = 6;
+		p1 = 3;
+		p2 = 2;
+		break;
+	case OFONO_RADIO_ACCESS_MODE_GSM:
+		val = 0;
+		break;
+	case OFONO_RADIO_ACCESS_MODE_UMTS:
+		val = 2;
+		break;
+	case OFONO_RADIO_ACCESS_MODE_LTE:
+		val = 3;
+		break;
+	case OFONO_RADIO_ACCESS_MODE_UMTS|OFONO_RADIO_ACCESS_MODE_GSM:
+		val = 1;
+		p1 = 2;
+		break;
+	case OFONO_RADIO_ACCESS_MODE_LTE|OFONO_RADIO_ACCESS_MODE_UMTS:
+		val = 4;
+		p1 = 3;
+		break;
+	case OFONO_RADIO_ACCESS_MODE_LTE|OFONO_RADIO_ACCESS_MODE_GSM:
+		val = 5;
+		p1 = 3;
+		break;
+	}
+
+	if (val == 6)
+		snprintf(buf, sizeof(buf), "AT^SXRAT=%u,%u,%u", val, p1, p2);
+	else if (val == 1 || val == 4 || val == 5)
+		snprintf(buf, sizeof(buf), "AT^SXRAT=%u,%u", val, p1);
+	else
+		snprintf(buf, sizeof(buf), "AT^SXRAT=%u", val);
+
+	if (g_at_chat_send(rsd->chat, buf, none_prefix,
+				sxrat_modify_cb, cbd, g_free) > 0)
+		return;
+
+	CALLBACK_WITH_FAILURE(cb, data);
+	g_free(cbd);
+}
+
+static void sxrat_support_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_radio_settings *rs = user_data;
+
+	DBG("ok %d", ok);
+
+	if (!ok) {
+		ofono_radio_settings_remove(rs);
+		return;
+	}
+
+	ofono_radio_settings_register(rs);
+}
+
+static int gemalto_radio_settings_probe(struct ofono_radio_settings *rs,
+					unsigned int vendor, void *data)
+{
+	GAtChat *chat = data;
+	struct radio_settings_data *rsd;
+
+	DBG("");
+
+	rsd = g_new0(struct radio_settings_data, 1);
+
+	rsd->chat = g_at_chat_clone(chat);
+
+	ofono_radio_settings_set_data(rs, rsd);
+
+	g_at_chat_send(rsd->chat, "AT^SXRAT=?", sxrat_prefix,
+			sxrat_support_cb, rs, NULL);
+
+	return 0;
+}
+
+static void gemalto_radio_settings_remove(struct ofono_radio_settings *rs)
+{
+	struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
+
+	DBG("");
+
+	ofono_radio_settings_set_data(rs, NULL);
+	g_at_chat_unref(rsd->chat);
+	g_free(rsd);
+}
+
+static const struct ofono_radio_settings_driver driver = {
+	.name			= "gemaltomodem",
+	.probe			= gemalto_radio_settings_probe,
+	.remove			= gemalto_radio_settings_remove,
+	.query_rat_mode		= gemalto_query_rat_mode,
+	.set_rat_mode		= gemalto_set_rat_mode
+};
+
+void gemalto_radio_settings_init(void)
+{
+	ofono_radio_settings_driver_register(&driver);
+}
+
+void gemalto_radio_settings_exit(void)
+{
+	ofono_radio_settings_driver_unregister(&driver);
+}
diff --git a/drivers/hsomodem/gprs-context.c b/drivers/hsomodem/gprs-context.c
index ca62248..aafcaa0 100644
--- a/drivers/hsomodem/gprs-context.c
+++ b/drivers/hsomodem/gprs-context.c
@@ -37,6 +37,7 @@
 #include "gatresult.h"
 
 #include "hsomodem.h"
+#include "src/missing.h"
 
 #define HSO_DISCONNECTED 0
 #define HSO_CONNECTED 1
@@ -128,7 +129,7 @@ static void hso_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
 		return;
 	}
 
-	ncbd = g_memdup(cbd, sizeof(struct cb_data));
+	ncbd = g_memdup2(cbd, sizeof(struct cb_data));
 
 	snprintf(buf, sizeof(buf), "AT_OWANCALL=%u,1,1", gcd->active_context);
 
diff --git a/drivers/hsomodem/radio-settings.c b/drivers/hsomodem/radio-settings.c
index 0a5e48f..c074d84 100644
--- a/drivers/hsomodem/radio-settings.c
+++ b/drivers/hsomodem/radio-settings.c
@@ -50,7 +50,7 @@ static void opsys_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
 	ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
-	enum ofono_radio_access_mode mode;
+	unsigned int mode;
 	struct ofono_error error;
 	GAtResultIter iter;
 	int value;
@@ -120,7 +120,7 @@ static void opsys_modify_cb(gboolean ok, GAtResult *result, gpointer user_data)
 }
 
 static void hso_set_rat_mode(struct ofono_radio_settings *rs,
-				enum ofono_radio_access_mode mode,
+				unsigned int mode,
 				ofono_radio_settings_rat_mode_set_cb_t cb,
 				void *data)
 {
diff --git a/drivers/huaweimodem/gprs-context.c b/drivers/huaweimodem/gprs-context.c
index de07b05..edc01ba 100644
--- a/drivers/huaweimodem/gprs-context.c
+++ b/drivers/huaweimodem/gprs-context.c
@@ -39,6 +39,7 @@
 #include "gattty.h"
 
 #include "huaweimodem.h"
+#include "src/missing.h"
 
 static const char *none_prefix[] = { NULL };
 static const char *dhcp_prefix[] = { "^DHCP:", NULL };
@@ -234,7 +235,7 @@ static void at_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
 		return;
 	}
 
-	ncbd = g_memdup(cbd, sizeof(struct cb_data));
+	ncbd = g_memdup2(cbd, sizeof(struct cb_data));
 
 	snprintf(buf, sizeof(buf), "AT^NDISDUP=%u,1", gcd->active_context);
 
diff --git a/drivers/huaweimodem/radio-settings.c b/drivers/huaweimodem/radio-settings.c
index 40431d3..60a15bc 100644
--- a/drivers/huaweimodem/radio-settings.c
+++ b/drivers/huaweimodem/radio-settings.c
@@ -104,7 +104,7 @@ static enum ofono_radio_band_gsm band_gsm_from_huawei(unsigned int band)
 	size_t i;
 
 	if (band == HUAWEI_BAND_ANY)
-		return OFONO_RADIO_BAND_UMTS_ANY;
+		return OFONO_RADIO_BAND_GSM_ANY;
 
 	for (i = ARRAY_SIZE(huawei_band_gsm_table) - 1; i > 0; i--) {
 		if (huawei_band_gsm_table[i].band_huawei & band)
@@ -134,7 +134,7 @@ static void syscfg_query_mode_cb(gboolean ok, GAtResult *result,
 {
 	struct cb_data *cbd = user_data;
 	ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
-	enum ofono_radio_access_mode mode;
+	unsigned int mode;
 	struct ofono_error error;
 	GAtResultIter iter;
 	int value;
@@ -182,7 +182,7 @@ static void syscfgex_query_mode_cb(gboolean ok, GAtResult *result,
 {
 	struct cb_data *cbd = user_data;
 	ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
-	enum ofono_radio_access_mode mode;
+	unsigned int mode;
 	struct ofono_error error;
 	GAtResultIter iter;
 	const char *acqorder;
@@ -207,6 +207,10 @@ static void syscfgex_query_mode_cb(gboolean ok, GAtResult *result,
 				strstr(acqorder, "02") &&
 				strstr(acqorder, "03")))
 		mode = OFONO_RADIO_ACCESS_MODE_ANY;
+	else if (strstr(acqorder, "0302"))
+		mode = (OFONO_RADIO_ACCESS_MODE_LTE | OFONO_RADIO_ACCESS_MODE_UMTS);
+	else if (strstr(acqorder, "0201"))
+		mode = (OFONO_RADIO_ACCESS_MODE_UMTS | OFONO_RADIO_ACCESS_MODE_GSM);
 	else if (strstr(acqorder, "03"))
 		mode = OFONO_RADIO_ACCESS_MODE_LTE;
 	else if (strstr(acqorder, "02"))
@@ -258,7 +262,7 @@ static void syscfgxx_modify_mode_cb(gboolean ok, GAtResult *result,
 }
 
 static void syscfg_set_rat_mode(struct radio_settings_data *rsd,
-				enum ofono_radio_access_mode mode,
+				unsigned int mode,
 				ofono_radio_settings_rat_mode_set_cb_t cb,
 				void *data)
 {
@@ -281,6 +285,8 @@ static void syscfg_set_rat_mode(struct radio_settings_data *rsd,
 		break;
 	case OFONO_RADIO_ACCESS_MODE_LTE:
 		goto error;
+	default:
+		goto error;
 	}
 
 	snprintf(buf, sizeof(buf), "AT^SYSCFG=%u,%u,40000000,2,4",
@@ -296,7 +302,7 @@ error:
 }
 
 static void syscfgex_set_rat_mode(struct radio_settings_data *rsd,
-				enum ofono_radio_access_mode mode,
+				unsigned int mode,
 				ofono_radio_settings_rat_mode_set_cb_t cb,
 				void *data)
 {
@@ -318,8 +324,15 @@ static void syscfgex_set_rat_mode(struct radio_settings_data *rsd,
 	case OFONO_RADIO_ACCESS_MODE_LTE:
 		acqorder = "03";
 		break;
+	default:
+		break;
 	}
 
+	if (mode == (OFONO_RADIO_ACCESS_MODE_LTE | OFONO_RADIO_ACCESS_MODE_UMTS))
+		acqorder = "0302";
+	else if (mode == (OFONO_RADIO_ACCESS_MODE_UMTS | OFONO_RADIO_ACCESS_MODE_GSM))
+		acqorder = "0201";
+
 	snprintf(buf, sizeof(buf), atcmd, acqorder);
 
 	if (g_at_chat_send(rsd->chat, buf, none_prefix,
@@ -331,7 +344,7 @@ static void syscfgex_set_rat_mode(struct radio_settings_data *rsd,
 }
 
 static void huawei_set_rat_mode(struct ofono_radio_settings *rs,
-				enum ofono_radio_access_mode mode,
+				unsigned int mode,
 				ofono_radio_settings_rat_mode_set_cb_t cb,
 				void *data)
 {
@@ -532,6 +545,7 @@ static void syscfgex_support_cb(gboolean ok, GAtResult *result,
 	if (!ok) {
 		g_at_chat_send(rsd->chat, "AT^SYSCFG=?", syscfg_prefix,
 					syscfg_support_cb, rs, NULL);
+		return;
 	}
 
 	rsd->syscfgex_cap = 1;
diff --git a/drivers/iceramodem/gprs-context.c b/drivers/iceramodem/gprs-context.c
index 41d9d9b..fdc7f84 100644
--- a/drivers/iceramodem/gprs-context.c
+++ b/drivers/iceramodem/gprs-context.c
@@ -40,6 +40,7 @@
 #include "gattty.h"
 
 #include "iceramodem.h"
+#include "src/missing.h"
 
 #define ICERA_DISCONNECTED	0
 #define ICERA_CONNECTED		1
@@ -284,7 +285,7 @@ static void at_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
 	if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
 		goto error;
 
-	ncbd = g_memdup(cbd, sizeof(struct cb_data));
+	ncbd = g_memdup2(cbd, sizeof(struct cb_data));
 
 	snprintf(buf, sizeof(buf), "AT%%IPDPACT=%u,1", gcd->active_context);
 
diff --git a/drivers/iceramodem/radio-settings.c b/drivers/iceramodem/radio-settings.c
index defa433..77498f3 100644
--- a/drivers/iceramodem/radio-settings.c
+++ b/drivers/iceramodem/radio-settings.c
@@ -51,7 +51,7 @@ static void ipsys_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
 	ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
-	enum ofono_radio_access_mode mode;
+	unsigned int mode;
 	struct ofono_error error;
 	GAtResultIter iter;
 	int value;
@@ -121,7 +121,7 @@ static void ipsys_modify_cb(gboolean ok, GAtResult *result, gpointer user_data)
 }
 
 static void icera_set_rat_mode(struct ofono_radio_settings *rs,
-				enum ofono_radio_access_mode mode,
+				unsigned int mode,
 				ofono_radio_settings_rat_mode_set_cb_t cb,
 				void *data)
 {
diff --git a/drivers/ifxmodem/radio-settings.c b/drivers/ifxmodem/radio-settings.c
index c132ba7..ebc21e0 100644
--- a/drivers/ifxmodem/radio-settings.c
+++ b/drivers/ifxmodem/radio-settings.c
@@ -50,7 +50,7 @@ static void xrat_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
 	ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
-	enum ofono_radio_access_mode mode;
+	unsigned int mode;
 	struct ofono_error error;
 	GAtResultIter iter;
 	int value, preferred;
@@ -120,8 +120,7 @@ static void xrat_modify_cb(gboolean ok, GAtResult *result, gpointer user_data)
 	cb(&error, cbd->data);
 }
 
-static void ifx_set_rat_mode(struct ofono_radio_settings *rs,
-				enum ofono_radio_access_mode mode,
+static void ifx_set_rat_mode(struct ofono_radio_settings *rs, unsigned int mode,
 				ofono_radio_settings_rat_mode_set_cb_t cb,
 				void *data)
 {
diff --git a/drivers/isimodem/radio-settings.c b/drivers/isimodem/radio-settings.c
index fa53300..f3f35e3 100644
--- a/drivers/isimodem/radio-settings.c
+++ b/drivers/isimodem/radio-settings.c
@@ -52,7 +52,7 @@ struct radio_data {
 	uint16_t quick_release:1;
 };
 
-static enum ofono_radio_access_mode isi_mode_to_ofono_mode(guint8 mode)
+static unsigned int isi_mode_to_ofono_mode(guint8 mode)
 {
 	switch (mode) {
 	case GSS_DUAL_RAT:
@@ -66,7 +66,7 @@ static enum ofono_radio_access_mode isi_mode_to_ofono_mode(guint8 mode)
 	}
 }
 
-static int ofono_mode_to_isi_mode(enum ofono_radio_access_mode mode)
+static int ofono_mode_to_isi_mode(unsigned int mode)
 {
 	switch (mode) {
 	case OFONO_RADIO_ACCESS_MODE_ANY:
@@ -184,8 +184,7 @@ error:
 	return;
 }
 
-static void isi_set_rat_mode(struct ofono_radio_settings *rs,
-				enum ofono_radio_access_mode mode,
+static void isi_set_rat_mode(struct ofono_radio_settings *rs, unsigned int mode,
 				ofono_radio_settings_rat_mode_set_cb_t cb,
 				void *data)
 {
diff --git a/drivers/mbimmodem/mbim-message.c b/drivers/mbimmodem/mbim-message.c
index 0c11765..9f972cb 100644
--- a/drivers/mbimmodem/mbim-message.c
+++ b/drivers/mbimmodem/mbim-message.c
@@ -501,7 +501,7 @@ static bool message_iter_next_entry_valist(struct mbim_message_iter *orig,
 			signature += 1;
 			indent += 1;
 
-			if (unlikely(indent > MAX_NESTING))
+			if (indent > MAX_NESTING)
 				return false;
 
 			if (!_iter_enter_struct(iter, &stack[indent - 1]))
@@ -511,7 +511,7 @@ static bool message_iter_next_entry_valist(struct mbim_message_iter *orig,
 
 			break;
 		case ')':
-			if (unlikely(indent == 0))
+			if (indent == 0)
 				return false;
 
 			signature += 1;
@@ -562,7 +562,7 @@ bool mbim_message_iter_next_entry(struct mbim_message_iter *iter, ...)
 	va_list args;
 	bool result;
 
-	if (unlikely(!iter))
+	if (!iter)
 		return false;
 
 	va_start(args, iter);
@@ -675,7 +675,7 @@ struct mbim_message *mbim_message_new(const uint8_t *uuid, uint32_t cid,
 
 struct mbim_message *mbim_message_ref(struct mbim_message *msg)
 {
-	if (unlikely(!msg))
+	if (!msg)
 		return NULL;
 
 	__sync_fetch_and_add(&msg->ref_count, 1);
@@ -687,7 +687,7 @@ void mbim_message_unref(struct mbim_message *msg)
 {
 	unsigned int i;
 
-	if (unlikely(!msg))
+	if (!msg)
 		return;
 
 	if (__sync_sub_and_fetch(&msg->ref_count, 1))
@@ -712,7 +712,7 @@ struct mbim_message *_mbim_message_build(const void *header,
 	msg = l_new(struct mbim_message, 1);
 
 	msg->ref_count = 1;
-	memcpy(msg->header, header, HEADER_SIZE);
+	memcpy(msg->header, header, sizeof(struct mbim_message_header));
 	msg->frags = frags;
 	msg->n_frags = n_frags;
 	msg->sealed = true;
@@ -760,10 +760,10 @@ uint32_t mbim_message_get_error(struct mbim_message *message)
 {
 	struct mbim_message_header *hdr;
 
-	if (unlikely(!message))
+	if (!message)
 		return false;
 
-	if (unlikely(!message->sealed))
+	if (!message->sealed)
 		return false;
 
 	hdr = (struct mbim_message_header *) message->header;
@@ -776,7 +776,7 @@ uint32_t mbim_message_get_error(struct mbim_message *message)
 
 uint32_t mbim_message_get_cid(struct mbim_message *message)
 {
-	if (unlikely(!message))
+	if (!message)
 		return false;
 
 	return message->cid;
@@ -784,7 +784,7 @@ uint32_t mbim_message_get_cid(struct mbim_message *message)
 
 const uint8_t *mbim_message_get_uuid(struct mbim_message *message)
 {
-	if (unlikely(!message))
+	if (!message)
 		return false;
 
 	return message->uuid;
@@ -800,10 +800,10 @@ bool mbim_message_get_arguments(struct mbim_message *message,
 	uint32_t type;
 	size_t begin;
 
-	if (unlikely(!message))
+	if (!message)
 		return false;
 
-	if (unlikely(!message->sealed))
+	if (!message->sealed)
 		return false;
 
 	hdr = (struct mbim_message_header *) message->header;
@@ -834,10 +834,10 @@ static bool _mbim_message_get_data(struct mbim_message *message,
 	size_t pos;
 	uint32_t i;
 
-	if (unlikely(!message))
+	if (!message)
 		return false;
 
-	if (unlikely(!message->sealed))
+	if (!message->sealed)
 		return false;
 
 	hdr = (struct mbim_message_header *) message->header;
@@ -1008,7 +1008,7 @@ struct mbim_message_builder *mbim_message_builder_new(struct mbim_message *msg)
 	uint32_t type;
 	struct container *container;
 
-	if (unlikely(!msg))
+	if (!msg)
 		return NULL;
 
 	if (msg->sealed)
@@ -1033,7 +1033,7 @@ void mbim_message_builder_free(struct mbim_message_builder *builder)
 {
 	uint32_t i;
 
-	if (unlikely(!builder))
+	if (!builder)
 		return;
 
 	mbim_message_unref(builder->message);
@@ -1060,10 +1060,10 @@ bool mbim_message_builder_append_basic(struct mbim_message_builder *builder,
 	size_t len;
 	uint16_t *utf16;
 
-	if (unlikely(!builder))
+	if (!builder)
 		return false;
 
-	if (unlikely(!strchr(simple_types, type)))
+	if (!strchr(simple_types, type))
 		return false;
 
 	alignment = get_alignment(type);
@@ -1168,16 +1168,16 @@ bool mbim_message_builder_append_bytes(struct mbim_message_builder *builder,
 	struct container *container = &builder->stack[builder->index];
 	size_t start;
 
-	if (unlikely(!builder))
+	if (!builder)
 		return false;
 
 	if (container->container_type == CONTAINER_TYPE_ARRAY) {
 		struct container *array;
 
-		if (unlikely(container->sigindex != 0))
+		if (container->sigindex != 0)
 			return false;
 
-		if (unlikely(container->signature[container->sigindex] != 'y'))
+		if (container->signature[container->sigindex] != 'y')
 			return false;
 
 		array = container;
@@ -1247,12 +1247,12 @@ bool mbim_message_builder_leave_struct(struct mbim_message_builder *builder)
 	struct container *array = NULL;
 	size_t start;
 
-	if (unlikely(builder->index == 0))
+	if (builder->index == 0)
 		return false;
 
 	container = &builder->stack[builder->index];
 
-	if (unlikely(container->container_type != CONTAINER_TYPE_STRUCT))
+	if (container->container_type != CONTAINER_TYPE_STRUCT)
 		return false;
 
 	builder->index -= 1;
@@ -1339,12 +1339,12 @@ bool mbim_message_builder_leave_array(struct mbim_message_builder *builder)
 {
 	struct container *container;
 
-	if (unlikely(builder->index == 0))
+	if (builder->index == 0)
 		return false;
 
 	container = &builder->stack[builder->index];
 
-	if (unlikely(container->container_type != CONTAINER_TYPE_ARRAY))
+	if (container->container_type != CONTAINER_TYPE_ARRAY)
 		return false;
 
 	builder->index -= 1;
@@ -1381,12 +1381,12 @@ bool mbim_message_builder_leave_databuf(struct mbim_message_builder *builder)
 	struct container *parent;
 	size_t start;
 
-	if (unlikely(builder->index == 0))
+	if (builder->index == 0)
 		return false;
 
 	container = &builder->stack[builder->index];
 
-	if (unlikely(container->container_type != CONTAINER_TYPE_DATABUF))
+	if (container->container_type != CONTAINER_TYPE_DATABUF)
 		return false;
 
 	builder->index -= 1;
@@ -1415,7 +1415,7 @@ struct mbim_message *mbim_message_builder_finalize(
 	struct container *root;
 	struct mbim_message_header *hdr;
 
-	if (unlikely(!builder))
+	if (!builder)
 		return NULL;
 
 	if (builder->index != 0)
@@ -1701,10 +1701,10 @@ bool mbim_message_set_arguments(struct mbim_message *message,
 	va_list args;
 	bool result;
 
-	if (unlikely(!message))
+	if (!message)
 		return false;
 
-	if (unlikely(message->sealed))
+	if (message->sealed)
 		return false;
 
 	if (!signature)
diff --git a/drivers/mbimmodem/mbim.c b/drivers/mbimmodem/mbim.c
index e5163bc..9de31eb 100644
--- a/drivers/mbimmodem/mbim.c
+++ b/drivers/mbimmodem/mbim.c
@@ -183,8 +183,7 @@ static struct mbim_message *message_assembly_add(
 	struct message_assembly_node *node;
 	struct mbim_message *message;
 
-	if (unlikely(type != MBIM_COMMAND_DONE &&
-				type != MBIM_INDICATE_STATUS_MSG))
+	if (type != MBIM_COMMAND_DONE && type != MBIM_INDICATE_STATUS_MSG)
 		return NULL;
 
 	node = l_queue_find(assembly->transactions,
@@ -894,7 +893,7 @@ struct mbim_device *mbim_device_new(int fd, uint32_t max_segment_size)
 {
 	struct mbim_device *device;
 
-	if (unlikely(fd < 0))
+	if (fd < 0)
 		return NULL;
 
 	device = l_new(struct mbim_device, 1);
@@ -926,7 +925,7 @@ struct mbim_device *mbim_device_new(int fd, uint32_t max_segment_size)
 
 struct mbim_device *mbim_device_ref(struct mbim_device *device)
 {
-	if (unlikely(!device))
+	if (!device)
 		return NULL;
 
 	__sync_fetch_and_add(&device->ref_count, 1);
@@ -936,7 +935,7 @@ struct mbim_device *mbim_device_ref(struct mbim_device *device)
 
 void mbim_device_unref(struct mbim_device *device)
 {
-	if (unlikely(!device))
+	if (!device)
 		return;
 
 	if (__sync_sub_and_fetch(&device->ref_count, 1))
@@ -966,7 +965,7 @@ void mbim_device_unref(struct mbim_device *device)
 
 bool mbim_device_shutdown(struct mbim_device *device)
 {
-	if (unlikely(!device))
+	if (!device)
 		return false;
 
 	l_io_set_read_handler(device->io, close_read_handler, device, NULL);
@@ -978,7 +977,7 @@ bool mbim_device_shutdown(struct mbim_device *device)
 
 bool mbim_device_set_max_outstanding(struct mbim_device *device, uint32_t max)
 {
-	if (unlikely(!device))
+	if (!device)
 		return false;
 
 	device->max_outstanding = max;
@@ -990,7 +989,7 @@ bool mbim_device_set_disconnect_handler(struct mbim_device *device,
 					void *user_data,
 					mbim_device_destroy_func_t destroy)
 {
-	if (unlikely(!device))
+	if (!device)
 		return false;
 
 	if (device->disconnect_destroy)
@@ -1007,7 +1006,7 @@ bool mbim_device_set_debug(struct mbim_device *device,
 				mbim_device_debug_func_t func, void *user_data,
 				mbim_device_destroy_func_t destroy)
 {
-	if (unlikely(!device))
+	if (!device)
 		return false;
 
 	if (device->debug_destroy)
@@ -1022,7 +1021,7 @@ bool mbim_device_set_debug(struct mbim_device *device,
 
 bool mbim_device_set_close_on_unref(struct mbim_device *device, bool do_close)
 {
-	if (unlikely(!device))
+	if (!device)
 		return false;
 
 	if (!device->io)
@@ -1037,7 +1036,7 @@ bool mbim_device_set_ready_handler(struct mbim_device *device,
 					void *user_data,
 					mbim_device_destroy_func_t destroy)
 {
-	if (unlikely(!device))
+	if (!device)
 		return false;
 
 	if (device->ready_destroy)
@@ -1058,7 +1057,7 @@ uint32_t mbim_device_send(struct mbim_device *device, uint32_t gid,
 {
 	struct pending_command *pending;
 
-	if (unlikely(!device || !message))
+	if (!device || !message)
 		return 0;
 
 	pending = l_new(struct pending_command, 1);
@@ -1088,7 +1087,7 @@ bool mbim_device_cancel(struct mbim_device *device, uint32_t tid)
 {
 	struct pending_command *pending;
 
-	if (unlikely(!device))
+	if (!device)
 		return false;
 
 	pending = l_queue_remove_if(device->pending_commands,
@@ -1112,7 +1111,7 @@ bool mbim_device_cancel(struct mbim_device *device, uint32_t tid)
 
 bool mbim_device_cancel_group(struct mbim_device *device, uint32_t gid)
 {
-	if (unlikely(!device))
+	if (!device)
 		return false;
 
 	l_queue_foreach_remove(device->pending_commands,
@@ -1135,7 +1134,7 @@ uint32_t mbim_device_register(struct mbim_device *device, uint32_t gid,
 	struct notification *notification;
 	uint32_t id;
 
-	if (unlikely(!device))
+	if (!device)
 		return 0;
 
 	id = device->next_notification;
@@ -1163,7 +1162,7 @@ bool mbim_device_unregister(struct mbim_device *device, uint32_t id)
 {
 	struct notification *notification;
 
-	if (unlikely(!device))
+	if (!device)
 		return false;
 
 	if (device->in_notify) {
@@ -1192,7 +1191,7 @@ bool mbim_device_unregister_group(struct mbim_device *device, uint32_t gid)
 	const struct l_queue_entry *entry;
 	bool r;
 
-	if (unlikely(!device))
+	if (!device)
 		return false;
 
 	if (!device->in_notify)
diff --git a/drivers/mbmmodem/gprs-context.c b/drivers/mbmmodem/gprs-context.c
index f873e28..d6c3a3c 100644
--- a/drivers/mbmmodem/gprs-context.c
+++ b/drivers/mbmmodem/gprs-context.c
@@ -38,6 +38,7 @@
 #include "gatresult.h"
 
 #include "mbmmodem.h"
+#include "src/missing.h"
 
 #define MBM_E2NAP_DISCONNECTED 0
 #define MBM_E2NAP_CONNECTED 1
@@ -345,7 +346,7 @@ static void mbm_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
 		return;
 	}
 
-	ncbd = g_memdup(cbd, sizeof(struct cb_data));
+	ncbd = g_memdup2(cbd, sizeof(struct cb_data));
 
 	snprintf(buf, sizeof(buf), "AT*ENAP=1,%u", gcd->active_context);
 
diff --git a/drivers/nwmodem/radio-settings.c b/drivers/nwmodem/radio-settings.c
index 13330e0..0a363f8 100644
--- a/drivers/nwmodem/radio-settings.c
+++ b/drivers/nwmodem/radio-settings.c
@@ -50,7 +50,7 @@ static void nwrat_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
 	ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
-	enum ofono_radio_access_mode mode;
+	unsigned int mode;
 	struct ofono_error error;
 	GAtResultIter iter;
 	int value;
@@ -117,8 +117,7 @@ static void nwrat_modify_cb(gboolean ok, GAtResult *result, gpointer user_data)
 	cb(&error, cbd->data);
 }
 
-static void nw_set_rat_mode(struct ofono_radio_settings *rs,
-				enum ofono_radio_access_mode mode,
+static void nw_set_rat_mode(struct ofono_radio_settings *rs, unsigned int mode,
 				ofono_radio_settings_rat_mode_set_cb_t cb,
 				void *data)
 {
diff --git a/drivers/qmimodem/gprs.c b/drivers/qmimodem/gprs.c
index 07adbe9..896a9e4 100644
--- a/drivers/qmimodem/gprs.c
+++ b/drivers/qmimodem/gprs.c
@@ -68,6 +68,28 @@ static bool extract_ss_info(struct qmi_result *result, int *status, int *tech)
 	return true;
 }
 
+static bool extract_dc_info(struct qmi_result *result, int *bearer_tech)
+{
+	const struct qmi_nas_data_capability *dc;
+	uint16_t len;
+	int i;
+
+	DBG("");
+
+	dc = qmi_result_get(result, QMI_NAS_RESULT_DATA_CAPABILITY_STATUS, &len);
+	if (!dc)
+		return false;
+
+	*bearer_tech = -1;
+	for (i = 0; i < dc->cap_count; i++) {
+		DBG("radio tech in use %d", dc->cap[i]);
+
+		*bearer_tech = qmi_nas_cap_to_bearer_tech(dc->cap[i]);
+	}
+
+	return true;
+}
+
 static void get_lte_attach_param_cb(struct qmi_result *result, void *user_data)
 {
 	struct ofono_gprs *gprs = user_data;
@@ -188,6 +210,7 @@ static int handle_ss_info(struct qmi_result *result, struct ofono_gprs *gprs)
 	struct gprs_data *data = ofono_gprs_get_data(gprs);
 	int status;
 	int tech;
+	int bearer_tech;
 
 	DBG("");
 
@@ -209,6 +232,10 @@ static int handle_ss_info(struct qmi_result *result, struct ofono_gprs *gprs)
 		data->last_auto_context_id = 0;
 	}
 
+	/* DC is optional so only notify on successful extraction */
+	if (extract_dc_info(result, &bearer_tech))
+		ofono_gprs_bearer_notify(gprs, bearer_tech);
+
 	return status;
 }
 
diff --git a/drivers/qmimodem/nas.c b/drivers/qmimodem/nas.c
index 48d7f11..630f901 100644
--- a/drivers/qmimodem/nas.c
+++ b/drivers/qmimodem/nas.c
@@ -36,3 +36,39 @@ int qmi_nas_rat_to_tech(uint8_t rat)
 
 	return -1;
 }
+
+int qmi_nas_cap_to_bearer_tech(int cap_tech)
+{
+
+	switch (cap_tech) {
+	case QMI_NAS_DATA_CAPABILITY_GSM:
+	case QMI_NAS_DATA_CAPABILITY_NONE:
+		return PACKET_BEARER_NONE;
+	case QMI_NAS_DATA_CAPABILITY_GPRS:
+		return PACKET_BEARER_GPRS;
+	case QMI_NAS_DATA_CAPABILITY_EDGE:
+		return PACKET_BEARER_EGPRS;
+	case QMI_NAS_DATA_CAPABILITY_EVDO_REV_0:
+	case QMI_NAS_DATA_CAPABILITY_EVDO_REV_A:
+	case QMI_NAS_DATA_CAPABILITY_EVDO_REV_B:
+		return PACKET_BEARER_UMTS;
+	case QMI_NAS_DATA_CAPABILITY_HSDPA:
+		return PACKET_BEARER_HSDPA;
+	case QMI_NAS_DATA_CAPABILITY_HSUPA:
+		return PACKET_BEARER_HSUPA;
+    case QMI_NAS_DATA_CAPABILITY_HSDPA_PLUS:
+    case QMI_NAS_DATA_CAPABILITY_DC_HSDPA_PLUS:
+		/*
+		 * HSPAP is HSPA+; which ofono doesn't define;
+		 * so, if differentiating HSPA and HSPA+ is
+		 * important, then ofono needs to be patched,
+		 * and we probably also need to introduce a
+		 * new indicator icon.
+		 */
+		return PACKET_BEARER_HSUPA_HSDPA;
+	case QMI_NAS_DATA_CAPABILITY_LTE:
+		return PACKET_BEARER_EPS;
+	default:
+		return PACKET_BEARER_NONE;
+	}
+}
diff --git a/drivers/qmimodem/nas.h b/drivers/qmimodem/nas.h
index 9f67707..30badab 100644
--- a/drivers/qmimodem/nas.h
+++ b/drivers/qmimodem/nas.h
@@ -135,6 +135,28 @@ struct qmi_nas_serving_system {
 	uint8_t radio_if[0];
 } __attribute__((__packed__));
 #define QMI_NAS_RESULT_ROAMING_STATUS		0x10	/* uint8 */
+
+#define QMI_NAS_RESULT_DATA_CAPABILITY_STATUS		0x11	/* uint8 */
+struct qmi_nas_data_capability {
+	uint8_t cap_count;
+	uint8_t cap[0];
+} __attribute__((__packed__));
+
+#define QMI_NAS_DATA_CAPABILITY_NONE          0x00
+#define QMI_NAS_DATA_CAPABILITY_GPRS          0x01
+#define QMI_NAS_DATA_CAPABILITY_EDGE          0x02
+#define QMI_NAS_DATA_CAPABILITY_HSDPA         0x03
+#define QMI_NAS_DATA_CAPABILITY_HSUPA         0x04
+#define QMI_NAS_DATA_CAPABILITY_WCDMA         0x05
+#define QMI_NAS_DATA_CAPABILITY_CDMA          0x06
+#define QMI_NAS_DATA_CAPABILITY_EVDO_REV_0    0x07
+#define QMI_NAS_DATA_CAPABILITY_EVDO_REV_A    0x08
+#define QMI_NAS_DATA_CAPABILITY_GSM           0x09
+#define QMI_NAS_DATA_CAPABILITY_EVDO_REV_B    0x0A
+#define QMI_NAS_DATA_CAPABILITY_LTE           0x0B
+#define QMI_NAS_DATA_CAPABILITY_HSDPA_PLUS    0x0C
+#define QMI_NAS_DATA_CAPABILITY_DC_HSDPA_PLUS 0x0D
+
 #define QMI_NAS_RESULT_CURRENT_PLMN		0x12
 struct qmi_nas_current_plmn {
 	uint16_t mcc;
@@ -188,3 +210,4 @@ struct qmi_nas_home_network {
 #define QMI_NAS_RESULT_SYSTEM_SELECTION_PREF_MODE	0x11
 
 int qmi_nas_rat_to_tech(uint8_t rat);
+int qmi_nas_cap_to_bearer_tech(int cap_tech);
diff --git a/drivers/qmimodem/network-registration.c b/drivers/qmimodem/network-registration.c
index 04f20c6..ecdc605 100644
--- a/drivers/qmimodem/network-registration.c
+++ b/drivers/qmimodem/network-registration.c
@@ -128,10 +128,18 @@ static bool extract_ss_info(struct qmi_result *result, int *status,
 
 	plmn = qmi_result_get(result, QMI_NAS_RESULT_CURRENT_PLMN, &len);
 	if (plmn) {
-		snprintf(operator->mcc, OFONO_MAX_MCC_LENGTH + 1, "%03d",
-						GUINT16_FROM_LE(plmn->mcc));
-		snprintf(operator->mnc, OFONO_MAX_MNC_LENGTH + 1, "%02d",
-						GUINT16_FROM_LE(plmn->mnc));
+		uint16_t mcc = GUINT16_FROM_LE(plmn->mcc);
+		uint16_t mnc = GUINT16_FROM_LE(plmn->mnc);
+
+		if (mcc > 999)
+			mcc = 999;
+
+		if (mnc > 999)
+			mnc = 999;
+
+		snprintf(operator->mcc, OFONO_MAX_MCC_LENGTH + 1, "%03d", mcc);
+		snprintf(operator->mnc, OFONO_MAX_MNC_LENGTH + 1, "%03d", mnc);
+
 		opname_len = plmn->desc_len;
 		if (opname_len > OFONO_MAX_OPERATOR_NAME_LENGTH)
 			opname_len = OFONO_MAX_OPERATOR_NAME_LENGTH;
@@ -311,11 +319,17 @@ static void scan_nets_cb(struct qmi_result *result, void *user_data)
 
 	for (i = 0; i < num; i++) {
 		const struct qmi_nas_network_info *netinfo = ptr + offset;
+		uint16_t mcc = GUINT16_FROM_LE(netinfo->mcc);
+		uint16_t mnc = GUINT16_FROM_LE(netinfo->mnc);
+
+		if (mcc > 999)
+			mcc = 999;
+
+		if (mnc > 999)
+			mnc = 999;
 
-		snprintf(list[i].mcc, OFONO_MAX_MCC_LENGTH + 1, "%03d",
-						GUINT16_FROM_LE(netinfo->mcc));
-		snprintf(list[i].mnc, OFONO_MAX_MNC_LENGTH + 1, "%02d",
-						GUINT16_FROM_LE(netinfo->mnc));
+		snprintf(list[i].mcc, OFONO_MAX_MCC_LENGTH + 1, "%03d", mcc);
+		snprintf(list[i].mnc, OFONO_MAX_MNC_LENGTH + 1, "%03d", mnc);
 		strncpy(list[i].name, netinfo->desc, netinfo->desc_len);
 		list[i].name[netinfo->desc_len] = '\0';
 
diff --git a/drivers/qmimodem/radio-settings.c b/drivers/qmimodem/radio-settings.c
index d6f911a..7ee9521 100644
--- a/drivers/qmimodem/radio-settings.c
+++ b/drivers/qmimodem/radio-settings.c
@@ -45,7 +45,7 @@ static void get_system_selection_pref_cb(struct qmi_result *result,
 {
 	struct cb_data *cbd = user_data;
 	ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
-	enum ofono_radio_access_mode mode = OFONO_RADIO_ACCESS_MODE_ANY;
+	unsigned int mode = OFONO_RADIO_ACCESS_MODE_ANY;
 	uint16_t pref;
 
 	DBG("");
@@ -68,6 +68,9 @@ static void get_system_selection_pref_cb(struct qmi_result *result,
 	case QMI_NAS_RAT_MODE_PREF_LTE:
 		mode = OFONO_RADIO_ACCESS_MODE_LTE;
 		break;
+	case QMI_NAS_RAT_MODE_PREF_GSM|QMI_NAS_RAT_MODE_PREF_LTE:
+		mode = OFONO_RADIO_ACCESS_MODE_GSM|OFONO_RADIO_ACCESS_MODE_LTE;
+		break;
 	}
 
 	CALLBACK_WITH_SUCCESS(cb, mode, cbd->data);
@@ -106,8 +109,7 @@ static void set_system_selection_pref_cb(struct qmi_result *result,
 	CALLBACK_WITH_SUCCESS(cb, cbd->data);
 }
 
-static void qmi_set_rat_mode(struct ofono_radio_settings *rs,
-			enum ofono_radio_access_mode mode,
+static void qmi_set_rat_mode(struct ofono_radio_settings *rs, unsigned int mode,
 			ofono_radio_settings_rat_mode_set_cb_t cb,
 			void *user_data)
 {
@@ -131,6 +133,9 @@ static void qmi_set_rat_mode(struct ofono_radio_settings *rs,
 	case OFONO_RADIO_ACCESS_MODE_LTE:
 		pref = QMI_NAS_RAT_MODE_PREF_LTE;
 		break;
+	case OFONO_RADIO_ACCESS_MODE_LTE|OFONO_RADIO_ACCESS_MODE_GSM:
+		pref = QMI_NAS_RAT_MODE_PREF_LTE|QMI_NAS_RAT_MODE_PREF_GSM;
+		break;
 	}
 
 	param = qmi_param_new();
diff --git a/drivers/qmimodem/ussd.c b/drivers/qmimodem/ussd.c
index 1e61303..e5399b9 100644
--- a/drivers/qmimodem/ussd.c
+++ b/drivers/qmimodem/ussd.c
@@ -67,6 +67,12 @@ static int convert_qmi_dcs_gsm_dcs(int qmi_dcs, int *gsm_dcs)
 	case QMI_USSD_DCS_ASCII:
 		*gsm_dcs = USSD_DCS_8BIT;
 		break;
+	case QMI_USSD_DCS_8BIT:
+		*gsm_dcs = USSD_DCS_8BIT;
+		break;
+	case QMI_USSD_DCS_UCS2:
+		*gsm_dcs = USSD_DCS_UCS2;
+		break;
 	default:
 		return 1;
 	}
@@ -74,6 +80,41 @@ static int convert_qmi_dcs_gsm_dcs(int qmi_dcs, int *gsm_dcs)
 	return 0;
 }
 
+static void async_ind(struct qmi_result *result, void *user_data)
+{
+	struct ofono_ussd *ussd = user_data;
+	const struct qmi_ussd_data *qmi_ussd;
+	uint8_t user_action_required = 0;
+	int notify_status = OFONO_USSD_STATUS_NOTIFY;
+	uint16_t len;
+	int gsm_dcs;
+
+	DBG("");
+
+	qmi_ussd = qmi_result_get(result, QMI_VOICE_PARAM_USSD_IND_DATA, &len);
+	if (qmi_ussd == NULL)
+		return;
+
+	if (validate_ussd_data(qmi_ussd, len))
+		goto error;
+
+	if (convert_qmi_dcs_gsm_dcs(qmi_ussd->dcs, &gsm_dcs))
+		goto error;
+
+	if (qmi_result_get_uint8(result, QMI_VOICE_PARAM_USSD_IND_USER_ACTION,
+					&user_action_required)) {
+		if (user_action_required == QMI_USSD_USER_ACTION_REQUIRED)
+			notify_status = OFONO_USSD_STATUS_ACTION_REQUIRED;
+	}
+
+	ofono_ussd_notify(ussd, notify_status, gsm_dcs,
+				qmi_ussd->data, qmi_ussd->length);
+	return;
+
+error:
+	ofono_ussd_notify(ussd, OFONO_USSD_STATUS_TERMINATED, 0, NULL, 0);
+}
+
 static void async_orig_ind(struct qmi_result *result, void *user_data)
 {
 	struct ofono_ussd *ussd = user_data;
@@ -141,6 +182,9 @@ static void create_voice_cb(struct qmi_service *service, void *user_data)
 
 	data->voice = qmi_service_ref(service);
 
+	qmi_service_register(data->voice, QMI_VOICE_USSD_IND,
+					async_ind, ussd, NULL);
+
 	qmi_service_register(data->voice, QMI_VOICE_ASYNC_ORIG_USSD,
 					async_orig_ind, ussd, NULL);
 
diff --git a/drivers/rilmodem/lte.c b/drivers/rilmodem/lte.c
index 6171896..b613d35 100644
--- a/drivers/rilmodem/lte.c
+++ b/drivers/rilmodem/lte.c
@@ -71,6 +71,7 @@ static void ril_lte_set_default_attach_info(const struct ofono_lte *lte,
 	struct cb_data *cbd = cb_data_new(cb, data, (struct ofono_lte *)lte);
 	struct parcel rilp;
 	char buf[OFONO_GPRS_MAX_APN_LENGTH + 1];
+	const char *proto = ril_util_gprs_proto_to_ril_string(info->proto);
 
 	DBG("%s", info->apn);
 
@@ -80,10 +81,11 @@ static void ril_lte_set_default_attach_info(const struct ofono_lte *lte,
 	if (strlen(info->apn) > 0) {
 		sprintf(buf, "%s", info->apn);
 		parcel_w_string(&rilp, buf);
-	} else
+	} else {
 		parcel_w_string(&rilp, "");	/* apn */
+	}
 
-	parcel_w_string(&rilp, "ip");		/* protocol */
+	parcel_w_string(&rilp, proto);	/* protocol */
 	parcel_w_int32(&rilp, 0);		/* auth type */
 	parcel_w_string(&rilp, "");		/* username */
 	parcel_w_string(&rilp, "");		/* password */
diff --git a/drivers/rilmodem/netmon.c b/drivers/rilmodem/netmon.c
index 7dd3a56..d279280 100644
--- a/drivers/rilmodem/netmon.c
+++ b/drivers/rilmodem/netmon.c
@@ -109,6 +109,9 @@ static int process_cellinfo_list(struct ril_msg *message,
 	int mcc, mnc;
 	int lac, cid, psc;
 	int rssi, ber;
+	int ci, pci, tac;
+	int rsrp, rsrq, rssnr;
+	int cqi, tadv;
 	char s_mcc[OFONO_MAX_MCC_LENGTH + 1];
 	char s_mnc[OFONO_MAX_MNC_LENGTH + 1];
 	int i, j;
@@ -201,7 +204,7 @@ static int process_cellinfo_list(struct ril_msg *message,
 
 		lac = (lac >= 0 && lac <= 65535) ? lac : -1;
 		cid = (cid >= 0 && cid <= 268435455) ? cid : -1;
-		psc = (psc >= 0 && rssi <= 511) ? psc : -1;
+		psc = (psc >= 0 && psc <= 511) ? psc : -1;
 		rssi = (rssi >= 0 && rssi <= 31) ? rssi : -1;
 		ber = (ber >= 0 && ber <= 7) ? ber : -1;
 
@@ -216,6 +219,54 @@ static int process_cellinfo_list(struct ril_msg *message,
 				OFONO_NETMON_INFO_BER, ber,
 				OFONO_NETMON_INFO_INVALID);
 
+	} else if (cell_type == NETMON_RIL_CELLINFO_TYPE_LTE) {
+		mcc = parcel_r_int32(&rilp);
+		mnc = parcel_r_int32(&rilp);
+		ci =  parcel_r_int32(&rilp);
+		pci = parcel_r_int32(&rilp);
+		tac = parcel_r_int32(&rilp);
+		rssi = parcel_r_int32(&rilp);
+		rsrp = parcel_r_int32(&rilp);
+		rsrq = parcel_r_int32(&rilp);
+		rssnr = parcel_r_int32(&rilp);
+		cqi = parcel_r_int32(&rilp);
+		tadv = parcel_r_int32(&rilp);
+
+		if (mcc >= 0 && mcc <= 999)
+		snprintf(s_mcc, sizeof(s_mcc), "%03d", mcc);
+		else
+		strcpy(s_mcc, "");
+
+		if (mnc >= 0 && mnc <= 999)
+		snprintf(s_mnc, sizeof(s_mnc), "%03d", mnc);
+		else
+		strcpy(s_mnc, "");
+
+		ci = (ci >= 0 && ci <= 268435455) ? ci : -1;
+		pci = (pci >= 0 && pci <= 503) ? pci : -1;
+		tac = (tac >= 0 && tac <= 65535) ? tac : -1;
+		rssi = (rssi >= 0 && rssi <= 31) ? rssi : -1;
+		rsrp = (rsrp >= 44 && rsrp <= 140) ? -rsrp : -1;
+		rsrq = (rsrq >= 3 && rsrq <= 20) ? -rsrq : -1;
+		rssnr = (rssnr >= -200 && rssnr <= 300) ? rssnr : -1;
+		cqi = (cqi >= 0 && cqi <= 15) ? cqi : -1;
+		tadv = (tadv >=0 && tadv <= 63) ? tadv : -1;
+
+		ofono_netmon_serving_cell_notify(netmon,
+				OFONO_NETMON_CELL_TYPE_LTE,
+				OFONO_NETMON_INFO_MCC, s_mcc,
+				OFONO_NETMON_INFO_MNC, s_mnc,
+				OFONO_NETMON_INFO_CI, ci,
+				OFONO_NETMON_INFO_PCI, pci,
+				OFONO_NETMON_INFO_TAC, tac,
+				OFONO_NETMON_INFO_RSSI, rssi,
+				OFONO_NETMON_INFO_RSRP, rsrp,
+				OFONO_NETMON_INFO_RSRQ, rsrq,
+				OFONO_NETMON_INFO_SNR, rssnr,
+				OFONO_NETMON_INFO_CQI, cqi,
+				OFONO_NETMON_INFO_TIMING_ADVANCE, tadv,
+				OFONO_NETMON_INFO_INVALID);
+
 	}
 
 	return OFONO_ERROR_TYPE_NO_ERROR;
@@ -307,8 +358,10 @@ static void periodic_update_cb(struct ril_msg *message, gpointer user_data)
 	struct cb_data *cbd = user_data;
 	ofono_netmon_cb_t cb = cbd->cb;
 
-	if (message->error != RIL_E_SUCCESS)
+	if (message->error != RIL_E_SUCCESS) {
 		CALLBACK_WITH_FAILURE(cb, cbd->data);
+		return;
+	}
 
 	CALLBACK_WITH_SUCCESS(cb, cbd->data);
 }
diff --git a/drivers/rilmodem/radio-settings.c b/drivers/rilmodem/radio-settings.c
index a2e25e0..446b97e 100644
--- a/drivers/rilmodem/radio-settings.c
+++ b/drivers/rilmodem/radio-settings.c
@@ -102,8 +102,7 @@ static void ril_set_rat_cb(struct ril_msg *message, gpointer user_data)
 	}
 }
 
-static void ril_set_rat_mode(struct ofono_radio_settings *rs,
-			enum ofono_radio_access_mode mode,
+static void ril_set_rat_mode(struct ofono_radio_settings *rs, unsigned int mode,
 			ofono_radio_settings_rat_mode_set_cb_t cb,
 			void *data)
 {
diff --git a/drivers/rilmodem/stk.c b/drivers/rilmodem/stk.c
index 47b469d..0bfb7c0 100644
--- a/drivers/rilmodem/stk.c
+++ b/drivers/rilmodem/stk.c
@@ -214,13 +214,13 @@ static int ril_stk_probe(struct ofono_stk *stk, unsigned int vendor,
 
 	ofono_stk_set_data(stk, data);
 
-	g_ril_register(ril, RIL_UNSOL_STK_PROACTIVE_COMMAND,
+	g_ril_register(data->ril, RIL_UNSOL_STK_PROACTIVE_COMMAND,
 					ril_stk_proactive_cmd_notify, stk);
 
-	g_ril_register(ril, RIL_UNSOL_STK_SESSION_END,
+	g_ril_register(data->ril, RIL_UNSOL_STK_SESSION_END,
 					ril_stk_session_end_notify, stk);
 
-	g_ril_register(ril, RIL_UNSOL_STK_EVENT_NOTIFY,
+	g_ril_register(data->ril, RIL_UNSOL_STK_EVENT_NOTIFY,
 					ril_stk_event_notify, stk);
 
 	g_ril_send(data->ril, RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING, NULL,
diff --git a/drivers/rilmodem/voicecall.c b/drivers/rilmodem/voicecall.c
index 13dc507..1fce4a3 100644
--- a/drivers/rilmodem/voicecall.c
+++ b/drivers/rilmodem/voicecall.c
@@ -177,7 +177,7 @@ static void clcc_poll_cb(struct ril_msg *message, gpointer user_data)
 		call->id = parcel_r_int32(&rilp);
 		call->phone_number.type = parcel_r_int32(&rilp);
 		parcel_r_int32(&rilp); /* isMpty */
-		parcel_r_int32(&rilp); /* isMT */
+		call->direction = parcel_r_int32(&rilp); /* isMT */
 		parcel_r_int32(&rilp); /* als */
 		call->type = parcel_r_int32(&rilp); /* isVoice */
 		parcel_r_int32(&rilp); /* isVoicePrivacy */
@@ -396,6 +396,8 @@ static void rild_cb(struct ril_msg *message, gpointer user_data)
 	 * DIAL_MODIFIED_TO_DIAL means redirection. The call we will see when
 	 * polling will have a different called number.
 	 */
+	vd->suppress_clcc_poll = FALSE;
+
 	if (message->error == RIL_E_SUCCESS ||
 			(g_ril_vendor(vd->ril) == OFONO_RIL_VENDOR_AOSP &&
 			message->error == RIL_E_DIAL_MODIFIED_TO_DIAL)) {
@@ -448,8 +450,10 @@ static void dial(struct ofono_voicecall *vc,
 
 	/* Send request to RIL */
 	if (g_ril_send(vd->ril, RIL_REQUEST_DIAL, &rilp,
-			rild_cb, cbd, g_free) > 0)
+			rild_cb, cbd, g_free) > 0) {
+		vd->suppress_clcc_poll = TRUE;
 		return;
+	}
 
 	g_free(cbd);
 	CALLBACK_WITH_FAILURE(cb, data);
@@ -594,6 +598,11 @@ void ril_call_state_notify(struct ril_msg *message, gpointer user_data)
 
 	g_ril_print_unsol_no_args(vd->ril, message);
 
+	if (vd->suppress_clcc_poll) {
+		DBG("suppress clcc poll!");
+		return;
+	}
+
 	/* Just need to request the call list again */
 	ril_poll_clcc(vc);
 
@@ -829,6 +838,7 @@ int ril_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
 	vd->vendor = vendor;
 	vd->cb = NULL;
 	vd->data = NULL;
+	vd->suppress_clcc_poll = FALSE;
 
 	clear_dtmf_queue(vd);
 
diff --git a/drivers/rilmodem/voicecall.h b/drivers/rilmodem/voicecall.h
index 31e120e..beb2510 100644
--- a/drivers/rilmodem/voicecall.h
+++ b/drivers/rilmodem/voicecall.h
@@ -31,6 +31,7 @@ struct ril_voicecall_data {
 	void *data;
 	gchar *tone_queue;
 	gboolean tone_pending;
+	gboolean suppress_clcc_poll;
 };
 
 int ril_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
diff --git a/drivers/stemodem/gprs-context.c b/drivers/stemodem/gprs-context.c
index c228480..4348e15 100644
--- a/drivers/stemodem/gprs-context.c
+++ b/drivers/stemodem/gprs-context.c
@@ -49,6 +49,7 @@
 #include "if_caif.h"
 #include "caif_rtnl.h"
 #include "common.h"
+#include "src/missing.h"
 
 #define MAX_DNS 2
 #define IP_ADDR_LEN 20
@@ -260,7 +261,7 @@ static void ste_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
 	snprintf(buf, sizeof(buf), "AT*EPPSD=1,%x,%u",
 			gcd->channel_id, gcd->active_context);
 
-	ncbd = g_memdup(cbd, sizeof(struct cb_data));
+	ncbd = g_memdup2(cbd, sizeof(struct cb_data));
 
 	if (g_at_chat_send(gcd->chat, buf, NULL,
 				ste_eppsd_up_cb, ncbd, g_free) > 0)
diff --git a/drivers/stemodem/radio-settings.c b/drivers/stemodem/radio-settings.c
index 5e726cd..d30acbc 100644
--- a/drivers/stemodem/radio-settings.c
+++ b/drivers/stemodem/radio-settings.c
@@ -56,7 +56,7 @@ enum ste_radio_mode {
 };
 
 static gboolean ste_mode_to_ofono_mode(enum ste_radio_mode stemode,
-				enum ofono_radio_access_mode *mode)
+				unsigned int *mode)
 {
 	switch (stemode) {
 	case STE_RADIO_ON:
@@ -76,7 +76,7 @@ static gboolean ste_mode_to_ofono_mode(enum ste_radio_mode stemode,
 	return FALSE;
 }
 
-static gboolean ofono_mode_to_ste_mode(enum ofono_radio_access_mode mode,
+static gboolean ofono_mode_to_ste_mode(unsigned int mode,
 				enum ste_radio_mode *stemode)
 {
 	switch (mode) {
@@ -100,7 +100,7 @@ static void rat_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
 	ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
-	enum ofono_radio_access_mode mode;
+	unsigned int mode;
 	struct ofono_error error;
 	GAtResultIter iter;
 	int value;
@@ -161,8 +161,7 @@ static void rat_modify_cb(gboolean ok, GAtResult *result, gpointer user_data)
 	CALLBACK_WITH_SUCCESS(cb, cbd->data);
 }
 
-static void ste_set_rat_mode(struct ofono_radio_settings *rs,
-				enum ofono_radio_access_mode mode,
+static void ste_set_rat_mode(struct ofono_radio_settings *rs, unsigned int mode,
 				ofono_radio_settings_rat_mode_set_cb_t cb,
 				void *data)
 {
diff --git a/drivers/swmodem/gprs-context.c b/drivers/swmodem/gprs-context.c
index 002053c..b63c895 100644
--- a/drivers/swmodem/gprs-context.c
+++ b/drivers/swmodem/gprs-context.c
@@ -40,6 +40,7 @@
 #include "gattty.h"
 
 #include "swmodem.h"
+#include "src/missing.h"
 
 static const char *none_prefix[] = { NULL };
 
@@ -132,7 +133,7 @@ static void at_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
 		return;
 	}
 
-	ncbd = g_memdup(cbd, sizeof(struct cb_data));
+	ncbd = g_memdup2(cbd, sizeof(struct cb_data));
 
 	snprintf(buf, sizeof(buf), "AT!SCACT=1,%u", gcd->active_context);
 
diff --git a/drivers/ubloxmodem/network-registration.c b/drivers/ubloxmodem/network-registration.c
index 25f239a..7c16e57 100644
--- a/drivers/ubloxmodem/network-registration.c
+++ b/drivers/ubloxmodem/network-registration.c
@@ -48,7 +48,6 @@
 static const char *none_prefix[] = { NULL };
 static const char *cmer_prefix[] = { "+CMER:", NULL };
 static const char *ureg_prefix[] = { "+UREG:", NULL };
-static const char *creg_prefix[] = { "+CREG:", NULL };
 
 struct netreg_data {
 	struct at_netreg_data at_data;
@@ -249,30 +248,28 @@ static gboolean is_registered(int status)
 		status == NETWORK_REGISTRATION_STATUS_ROAMING;
 }
 
-static void ublox_creg_cb(gboolean ok, GAtResult *result,
-				gpointer user_data)
+static void registration_status_cb(const struct ofono_error *error,
+				   int status, int lac, int ci, int tech,
+				   void *user_data)
 {
 	struct tech_query *tq = user_data;
 	struct netreg_data *nd = ofono_netreg_get_data(tq->netreg);
-	int status;
-	int lac;
-	int ci;
-	int tech;
-
-	nd->updating_status = false;
-
-	if (!ok)
-		return;
-
-	if (at_util_parse_reg(result, "+CREG:", NULL, &status,
-				&lac, &ci, &tech, OFONO_VENDOR_GENERIC) == FALSE)
-		return;
+	struct ofono_netreg *netreg = tq->netreg;
 
 	/* The query provided a tech, use that */
 	if (is_registered(status) && tq->tech != -1)
 		tech = tq->tech;
 
-	ofono_netreg_status_notify(tq->netreg, status, lac, ci, tech);
+	g_free(tq);
+
+	nd->updating_status = false;
+
+	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+		DBG("Error during registration status query");
+		return;
+	}
+
+	ofono_netreg_status_notify(netreg, status, lac, ci, tech);
 }
 
 static void ublox_ureg_cb(gboolean ok, GAtResult *result,
@@ -282,7 +279,7 @@ static void ublox_ureg_cb(gboolean ok, GAtResult *result,
 	struct netreg_data *nd = ofono_netreg_get_data(tq->netreg);
 	GAtResultIter iter;
 	gint enabled, state;
-	int tech = tq->tech;
+	int tech = -1;
 
 	nd->updating_status = false;
 
@@ -291,21 +288,23 @@ static void ublox_ureg_cb(gboolean ok, GAtResult *result,
 
 	g_at_result_iter_init(&iter, result);
 
-	if (!g_at_result_iter_next(&iter, "+UREG:"))
-		return;
+	while (g_at_result_iter_next(&iter, "+UREG:")) {
+		if (!g_at_result_iter_next_number(&iter, &enabled))
+			return;
 
-	if (!g_at_result_iter_next_number(&iter, &enabled))
-		return;
+		/* Sometimes we get an unsolicited UREG here, skip it */
+		if (!g_at_result_iter_next_number(&iter, &state))
+			continue;
 
-	if (!g_at_result_iter_next_number(&iter, &state))
-		return;
+		tech = ublox_ureg_state_to_tech(state);
+		break;
+	}
 
-	tech = ublox_ureg_state_to_tech(state);
+error:
 	if (tech < 0)
 		/* No valid UREG status, we have to trust CREG... */
 		tech = tq->tech;
 
-error:
 	ofono_netreg_status_notify(tq->netreg,
 			tq->status, tq->lac, tq->ci, tech);
 }
@@ -334,13 +333,8 @@ static void ureg_notify(GAtResult *result, gpointer user_data)
 	tq->tech = ublox_ureg_state_to_tech(state);
 	tq->netreg = netreg;
 
-	if (g_at_chat_send(nd->at_data.chat, "AT+CREG?", creg_prefix,
-			ublox_creg_cb, tq, g_free) > 0) {
-		nd->updating_status = true;
-		return;
-	}
-
-	g_free(tq);
+	nd->updating_status = true;
+	at_registration_status(netreg, registration_status_cb, tq);
 }
 
 static void creg_notify(GAtResult *result, gpointer user_data)
diff --git a/drivers/xmm7modem/radio-settings.c b/drivers/xmm7modem/radio-settings.c
index c7c2ce0..19557bd 100644
--- a/drivers/xmm7modem/radio-settings.c
+++ b/drivers/xmm7modem/radio-settings.c
@@ -50,7 +50,7 @@ static void xact_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
 	ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
-	enum ofono_radio_access_mode mode;
+	unsigned int mode;
 	struct ofono_error error;
 	GAtResultIter iter;
 	int value, preferred;
@@ -133,8 +133,7 @@ static void xact_modify_cb(gboolean ok, GAtResult *result, gpointer user_data)
 	cb(&error, cbd->data);
 }
 
-static void xmm_set_rat_mode(struct ofono_radio_settings *rs,
-				enum ofono_radio_access_mode mode,
+static void xmm_set_rat_mode(struct ofono_radio_settings *rs, unsigned int mode,
 				ofono_radio_settings_rat_mode_set_cb_t cb,
 				void *data)
 {
diff --git a/drivers/ztemodem/radio-settings.c b/drivers/ztemodem/radio-settings.c
index 6a2b1fc..e83f455 100644
--- a/drivers/ztemodem/radio-settings.c
+++ b/drivers/ztemodem/radio-settings.c
@@ -50,7 +50,7 @@ static void zsnt_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
 	ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
-	enum ofono_radio_access_mode mode;
+	unsigned int mode;
 	struct ofono_error error;
 	GAtResultIter iter;
 	int value;
@@ -117,8 +117,7 @@ static void zsnt_modify_cb(gboolean ok, GAtResult *result, gpointer user_data)
 	cb(&error, cbd->data);
 }
 
-static void zte_set_rat_mode(struct ofono_radio_settings *rs,
-				enum ofono_radio_access_mode mode,
+static void zte_set_rat_mode(struct ofono_radio_settings *rs, unsigned int mode,
 				ofono_radio_settings_rat_mode_set_cb_t cb,
 				void *data)
 {
diff --git a/ell/checksum.c b/ell/checksum.c
index 729399e..c71205a 100644
--- a/ell/checksum.c
+++ b/ell/checksum.c
@@ -31,7 +31,7 @@
 #include <sys/socket.h>
 #include <stdio.h>
 
-#include "util.h"
+#include "useful.h"
 #include "checksum.h"
 #include "private.h"
 
diff --git a/ell/checksum.h b/ell/checksum.h
index 08d74b7..531fcb0 100644
--- a/ell/checksum.h
+++ b/ell/checksum.h
@@ -64,7 +64,7 @@ ssize_t l_checksum_get_digest(struct l_checksum *checksum,
 char *l_checksum_get_string(struct l_checksum *checksum);
 
 bool l_checksum_is_supported(enum l_checksum_type type, bool check_hmac);
-bool l_checksum_cmac_aes_supported();
+bool l_checksum_cmac_aes_supported(void);
 
 ssize_t l_checksum_digest_length(enum l_checksum_type type);
 
diff --git a/ell/cleanup.h b/ell/cleanup.h
new file mode 100644
index 0000000..89b1981
--- /dev/null
+++ b/ell/cleanup.h
@@ -0,0 +1,27 @@
+/*
+ *
+ *  Embedded Linux library
+ *
+ *  Copyright (C) 2021  Intel Corporation. All rights reserved.
+ *
+ *  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.1 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#pragma once
+
+#define DEFINE_CLEANUP_FUNC(func)			\
+	inline __attribute__((always_inline))		\
+	void func ## _cleanup(void *p) { func(*(void **) p); }
diff --git a/ell/gpio.c b/ell/gpio.c
index c470ba9..bec8a8a 100644
--- a/ell/gpio.c
+++ b/ell/gpio.c
@@ -35,7 +35,7 @@
 
 #include "private.h"
 #include "strv.h"
-#include "util.h"
+#include "useful.h"
 #include "gpio.h"
 
 struct l_gpio_chip {
diff --git a/ell/idle.c b/ell/idle.c
index 5c66280..c938052 100644
--- a/ell/idle.c
+++ b/ell/idle.c
@@ -25,9 +25,12 @@
 #endif
 
 #include <unistd.h>
+#include <stdbool.h>
+#include <stdint.h>
 
-#include "util.h"
+#include "useful.h"
 #include "idle.h"
+#include "main-private.h"
 #include "private.h"
 
 /**
diff --git a/ell/io.c b/ell/io.c
index 878060c..0a203cb 100644
--- a/ell/io.c
+++ b/ell/io.c
@@ -26,9 +26,11 @@
 
 #include <errno.h>
 #include <unistd.h>
+#include <stdbool.h>
 #include <sys/epoll.h>
 
-#include "util.h"
+#include "useful.h"
+#include "main-private.h"
 #include "io.h"
 #include "private.h"
 
@@ -111,14 +113,6 @@ static void io_callback(int fd, uint32_t events, void *user_data)
 {
 	struct l_io *io = user_data;
 
-	if (unlikely(events & (EPOLLERR | EPOLLHUP))) {
-		l_util_debug(io->debug_handler, io->debug_data,
-						"disconnect event <%p>", io);
-		watch_remove(io->fd);
-		io_closed(io);
-		return;
-	}
-
 	if ((events & EPOLLIN) && io->read_handler) {
 		l_util_debug(io->debug_handler, io->debug_data,
 						"read event <%p>", io);
@@ -142,6 +136,17 @@ static void io_callback(int fd, uint32_t events, void *user_data)
 		}
 	}
 
+	if (unlikely(events & (EPOLLERR | EPOLLHUP))) {
+		bool close_on_destroy = io->close_on_destroy;
+		int fd = io->fd;
+
+		l_util_debug(io->debug_handler, io->debug_data,
+						"disconnect event <%p>", io);
+		io_closed(io);
+		watch_remove(fd, !close_on_destroy);
+		return;
+	}
+
 	if ((events & EPOLLOUT) && io->write_handler) {
 		l_util_debug(io->debug_handler, io->debug_data,
 						"write event <%p>", io);
@@ -209,7 +214,7 @@ LIB_EXPORT void l_io_destroy(struct l_io *io)
 		return;
 
 	if (io->fd != -1)
-		watch_remove(io->fd);
+		watch_remove(io->fd, !io->close_on_destroy);
 
 	io_closed(io);
 
diff --git a/ell/log.c b/ell/log.c
index f3f4b0c..05af3e5 100644
--- a/ell/log.c
+++ b/ell/log.c
@@ -139,6 +139,7 @@ LIB_EXPORT void l_log_set_null(void)
 	log_func = log_null;
 }
 
+__attribute__((format(printf, 5, 0)))
 static void log_stderr(int priority, const char *file, const char *line,
 			const char *func, const char *format, va_list ap)
 {
@@ -157,6 +158,7 @@ LIB_EXPORT void l_log_set_stderr(void)
 	log_func = log_stderr;
 }
 
+__attribute__((format(printf, 5, 0)))
 static void log_syslog(int priority, const char *file, const char *line,
 			const char *func, const char *format, va_list ap)
 {
@@ -205,6 +207,7 @@ LIB_EXPORT void l_log_set_syslog(void)
 	log_func = log_syslog;
 }
 
+__attribute__((format(printf, 5, 0)))
 static void log_journal(int priority, const char *file, const char *line,
 			const char *func, const char *format, va_list ap)
 {
@@ -323,7 +326,7 @@ LIB_EXPORT void l_log_with_location(int priority,
 
 static const char *debug_pattern;
 
-void debug_enable(struct l_debug_desc *start, struct l_debug_desc *stop)
+static void debug_enable(struct l_debug_desc *start, struct l_debug_desc *stop)
 {
 	struct l_debug_desc *desc;
 	char *pattern_copy;
@@ -347,7 +350,7 @@ void debug_enable(struct l_debug_desc *start, struct l_debug_desc *stop)
 	}
 }
 
-void debug_disable(struct l_debug_desc *start, struct l_debug_desc *stop)
+static void debug_disable(struct l_debug_desc *start, struct l_debug_desc *stop)
 {
 	struct l_debug_desc *desc;
 
@@ -369,7 +372,6 @@ LIB_EXPORT void l_debug_add_section(struct l_debug_desc *start,
 					struct l_debug_desc *end)
 {
 	const struct l_queue_entry *entry;
-	const struct debug_section *section;
 	struct debug_section *new_section;
 
 	if (!debug_sections) {
@@ -379,7 +381,7 @@ LIB_EXPORT void l_debug_add_section(struct l_debug_desc *start,
 
 	for (entry = l_queue_get_entries(debug_sections); entry;
 					entry = entry->next) {
-		section = entry->data;
+		const struct debug_section *section = entry->data;
 
 		if (section->start == start && section->end == end)
 			return;
@@ -406,7 +408,6 @@ LIB_EXPORT void l_debug_enable_full(const char *pattern,
 					struct l_debug_desc *end)
 {
 	const struct l_queue_entry *entry;
-	const struct debug_section *section;
 
 	if (!pattern)
 		return;
@@ -417,7 +418,8 @@ LIB_EXPORT void l_debug_enable_full(const char *pattern,
 
 	for (entry = l_queue_get_entries(debug_sections); entry;
 					entry = entry->next) {
-		section = entry->data;
+		const struct debug_section *section = entry->data;
+
 		debug_enable(section->start, section->end);
 	}
 }
@@ -430,11 +432,11 @@ LIB_EXPORT void l_debug_enable_full(const char *pattern,
 LIB_EXPORT void l_debug_disable(void)
 {
 	const struct l_queue_entry *entry;
-	const struct debug_section *section;
 
 	for (entry = l_queue_get_entries(debug_sections); entry;
 					entry = entry->next) {
-		section = entry->data;
+		const struct debug_section *section = entry->data;
+
 		debug_disable(section->start, section->end);
 	}
 
diff --git a/ell/log.h b/ell/log.h
index 19bf10b..9ae40c0 100644
--- a/ell/log.h
+++ b/ell/log.h
@@ -61,12 +61,20 @@ struct l_debug_desc {
 	unsigned int flags;
 } __attribute__((aligned(8)));
 
+/*
+ * Set the retain attribute so that the section cannot be discarded by ld
+ * --gc-sections -z start-stop-gc. Older compilers would warn for the unknown
+ *  attribute, so just disable -Wattributes.
+ */
 #define L_DEBUG_SYMBOL(symbol, format, ...) do { \
+_Pragma("GCC diagnostic push") \
+_Pragma("GCC diagnostic ignored \"-Wattributes\"") \
 	static struct l_debug_desc symbol \
-	__attribute__((used, section("__ell_debug"), aligned(8))) = { \
+	__attribute__((used, retain, section("__ell_debug"), aligned(8))) = { \
 		.file = __FILE__, .func = __func__, \
 		.flags = L_DEBUG_FLAG_DEFAULT, \
 	}; \
+_Pragma("GCC diagnostic pop") \
 	if (symbol.flags & L_DEBUG_FLAG_PRINT) \
 		l_log(L_LOG_DEBUG, "%s:%s() " format, __FILE__, \
 					__func__ , ##__VA_ARGS__); \
@@ -74,7 +82,7 @@ struct l_debug_desc {
 
 void l_debug_enable_full(const char *pattern,
 				struct l_debug_desc *start,
-				struct l_debug_desc *stop);
+				struct l_debug_desc *end);
 void l_debug_add_section(struct l_debug_desc *start,
 					struct l_debug_desc *end);
 
diff --git a/ell/main-private.h b/ell/main-private.h
new file mode 100644
index 0000000..bca74de
--- /dev/null
+++ b/ell/main-private.h
@@ -0,0 +1,38 @@
+/*
+ *
+ *  Embedded Linux library
+ *
+ *  Copyright (C) 2021  Intel Corporation. All rights reserved.
+ *
+ *  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.1 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+typedef void (*watch_event_cb_t) (int fd, uint32_t events, void *user_data);
+typedef void (*watch_destroy_cb_t) (void *user_data);
+
+typedef void (*idle_event_cb_t) (void *user_data);
+typedef void (*idle_destroy_cb_t) (void *user_data);
+
+int watch_add(int fd, uint32_t events, watch_event_cb_t callback,
+				void *user_data, watch_destroy_cb_t destroy);
+int watch_modify(int fd, uint32_t events, bool force);
+int watch_remove(int fd, bool epoll_del);
+int watch_clear(int fd);
+
+#define IDLE_FLAG_NO_WARN_DANGLING 0x10000000
+int idle_add(idle_event_cb_t callback, void *user_data, uint32_t flags,
+		idle_destroy_cb_t destroy);
+void idle_remove(int id);
diff --git a/ell/main.c b/ell/main.c
index 3868f32..1a6cd60 100644
--- a/ell/main.c
+++ b/ell/main.c
@@ -38,8 +38,9 @@
 #include "signal.h"
 #include "queue.h"
 #include "log.h"
-#include "util.h"
+#include "useful.h"
 #include "main.h"
+#include "main-private.h"
 #include "private.h"
 #include "timeout.h"
 
@@ -224,17 +225,21 @@ int watch_clear(int fd)
 	return 0;
 }
 
-int watch_remove(int fd)
+int watch_remove(int fd, bool epoll_del)
 {
 	int err = watch_clear(fd);
 
 	if (err < 0)
 		return err;
 
+	if (!epoll_del)
+		goto done;
+
 	err = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
 	if (err < 0)
 		return -errno;
 
+done:
 	return err;
 }
 
diff --git a/ell/main.h b/ell/main.h
index 99b34ad..51e051a 100644
--- a/ell/main.h
+++ b/ell/main.h
@@ -42,7 +42,7 @@ typedef void (*l_main_signal_cb_t) (uint32_t signo, void *user_data);
 
 int l_main_run_with_signal(l_main_signal_cb_t callback, void *user_data);
 
-int l_main_get_epoll_fd();
+int l_main_get_epoll_fd(void);
 
 #ifdef __cplusplus
 }
diff --git a/ell/missing.h b/ell/missing.h
index 37d5586..2a3e647 100644
--- a/ell/missing.h
+++ b/ell/missing.h
@@ -21,6 +21,7 @@
  */
 
 #include <sys/syscall.h>
+#include <sys/socket.h>
 
 #ifndef __NR_getrandom
 #  if defined __x86_64__
@@ -62,3 +63,17 @@ static inline void explicit_bzero(void *s, size_t n)
 	__asm__ __volatile__ ("" : : "r"(s) : "memory");
 }
 #endif
+
+#ifndef SO_BINDTOIFINDEX
+#define SO_BINDTOIFINDEX 62
+#endif
+
+#ifndef HAVE_RAWMEMCHR
+static inline void *rawmemchr(const void *s, int c)
+{
+_Pragma("GCC diagnostic push")
+_Pragma("GCC diagnostic ignored \"-Wstringop-overflow=\"")
+	return memchr(s, c, (size_t) -1);
+_Pragma("GCC diagnostic pop")
+}
+#endif
diff --git a/ell/private.h b/ell/private.h
index 4dc48d7..bd328a5 100644
--- a/ell/private.h
+++ b/ell/private.h
@@ -20,37 +20,6 @@
  *
  */
 
-#include <stdint.h>
-#include <errno.h>
-
 #include <ell/util.h>
 
-#define uninitialized_var(x) x = x
-
-#define align_len(len, boundary) (((len)+(boundary)-1) & ~((boundary)-1))
-
 #define LIB_EXPORT __attribute__ ((visibility("default")))
-
-struct l_debug_desc;
-
-void debug_enable(struct l_debug_desc *start, struct l_debug_desc *stop);
-void debug_disable(struct l_debug_desc *start, struct l_debug_desc *stop);
-
-void plugin_update_debug(void);
-
-typedef void (*watch_event_cb_t) (int fd, uint32_t events, void *user_data);
-typedef void (*watch_destroy_cb_t) (void *user_data);
-
-typedef void (*idle_event_cb_t) (void *user_data);
-typedef void (*idle_destroy_cb_t) (void *user_data);
-
-int watch_add(int fd, uint32_t events, watch_event_cb_t callback,
-				void *user_data, watch_destroy_cb_t destroy);
-int watch_modify(int fd, uint32_t events, bool force);
-int watch_remove(int fd);
-int watch_clear(int fd);
-
-#define IDLE_FLAG_NO_WARN_DANGLING 0x10000000
-int idle_add(idle_event_cb_t callback, void *user_data, uint32_t flags,
-		idle_destroy_cb_t destroy);
-void idle_remove(int id);
diff --git a/ell/queue.c b/ell/queue.c
index 0f6ed3e..fb145da 100644
--- a/ell/queue.c
+++ b/ell/queue.c
@@ -24,9 +24,9 @@
 #include <config.h>
 #endif
 
-#include "util.h"
 #include "queue.h"
 #include "private.h"
+#include "useful.h"
 
 /**
  * SECTION:queue
@@ -271,8 +271,9 @@ LIB_EXPORT void *l_queue_peek_tail(struct l_queue *queue)
  * @user_data: user data given to compare function
  *
  * Inserts @data pointer at a position in the queue determined by the
- * compare @function.  @function should return > 0 if the @data (first
- * parameter) should be inserted after the current entry (second parameter).
+ * compare @function.  @function should return >= 0 if the @data (first
+ * parameter) should be inserted after the current entry (second parameter)
+ * and should return < 0 if before it.
  *
  * Returns: #true when data has been added and #false in case of failure
  **/
@@ -533,10 +534,10 @@ LIB_EXPORT void *l_queue_remove_if(struct l_queue *queue,
 			queue->entries--;
 
 			return data;
-		} else {
-			prev = entry;
-			entry = entry->next;
 		}
+
+		prev = entry;
+		entry = entry->next;
 	}
 
 	return NULL;
@@ -580,7 +581,7 @@ LIB_EXPORT bool l_queue_isempty(struct l_queue *queue)
  * Returns: A pointer to the head of the queue.
  **/
 LIB_EXPORT const struct l_queue_entry *l_queue_get_entries(
-							struct l_queue *queue)
+						const struct l_queue *queue)
 {
 	if (unlikely(!queue))
 		return NULL;
diff --git a/ell/queue.h b/ell/queue.h
index ff46a08..94b294f 100644
--- a/ell/queue.h
+++ b/ell/queue.h
@@ -73,7 +73,7 @@ unsigned int l_queue_foreach_remove(struct l_queue *queue,
 unsigned int l_queue_length(struct l_queue *queue);
 bool l_queue_isempty(struct l_queue *queue);
 
-const struct l_queue_entry *l_queue_get_entries(struct l_queue *queue);
+const struct l_queue_entry *l_queue_get_entries(const struct l_queue *queue);
 
 #ifdef __cplusplus
 }
diff --git a/ell/random.h b/ell/random.h
index 49a4637..acc278b 100644
--- a/ell/random.h
+++ b/ell/random.h
@@ -32,7 +32,7 @@ extern "C" {
 #include <stdint.h>
 
 bool l_getrandom(void *buf, size_t len);
-bool l_getrandom_is_supported();
+bool l_getrandom_is_supported(void);
 
 uint32_t l_getrandom_uint32(void);
 
diff --git a/ell/string.c b/ell/string.c
index 37806e3..d36abdd 100644
--- a/ell/string.c
+++ b/ell/string.c
@@ -26,10 +26,10 @@
 
 #include <stdio.h>
 
-#include "util.h"
 #include "strv.h"
 #include "string.h"
 #include "private.h"
+#include "useful.h"
 
 /**
  * SECTION:string
diff --git a/ell/string.h b/ell/string.h
index c1948ec..e1faa7d 100644
--- a/ell/string.h
+++ b/ell/string.h
@@ -24,6 +24,7 @@
 #define __ELL_STRING_H
 
 #include <stdarg.h>
+#include <ell/cleanup.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -33,6 +34,7 @@ struct l_string;
 
 struct l_string *l_string_new(size_t initial_length);
 void l_string_free(struct l_string *string);
+DEFINE_CLEANUP_FUNC(l_string_free);
 char *l_string_unwrap(struct l_string *string);
 
 struct l_string *l_string_append(struct l_string *dest, const char *src);
@@ -41,7 +43,8 @@ struct l_string *l_string_append_fixed(struct l_string *dest, const char *src,
 					size_t max);
 
 void l_string_append_vprintf(struct l_string *dest,
-					const char *format, va_list args);
+					const char *format, va_list args)
+					__attribute__((format(printf, 2, 0)));
 void l_string_append_printf(struct l_string *dest, const char *format, ...)
 					__attribute__((format(printf, 2, 3)));
 
diff --git a/ell/strv.c b/ell/strv.c
index 7c7dbf5..1343519 100644
--- a/ell/strv.c
+++ b/ell/strv.c
@@ -27,9 +27,9 @@
 #define _GNU_SOURCE
 #include <string.h>
 
-#include "util.h"
 #include "strv.h"
 #include "private.h"
+#include "useful.h"
 
 /**
  * SECTION:strv
@@ -359,3 +359,23 @@ LIB_EXPORT char **l_strv_copy(char **str_array)
 
 	return copy;
 }
+
+/**
+ * l_strv_eq:
+ * @a: a %NULL terminated array of strings or %NULL
+ * @b: another %NULL terminated array of strings or %NULL
+ *
+ * Returns: Whether @a and @b's contents are identical, including the
+ * order, or @a and @b are both %NULL.
+ */
+LIB_EXPORT bool l_strv_eq(char **a, char **b)
+{
+	if (!a || !b)
+		return a == b;
+
+	for (; *a; a++, b++)
+		if (!*b || strcmp(*a, *b))
+			return false;
+
+	return !*b;
+}
diff --git a/ell/strv.h b/ell/strv.h
index b0ec17c..6de81db 100644
--- a/ell/strv.h
+++ b/ell/strv.h
@@ -23,6 +23,10 @@
 #ifndef __ELL_STRV_H
 #define __ELL_STRV_H
 
+#include <stdarg.h>
+#include <stdbool.h>
+#include <ell/cleanup.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -34,14 +38,17 @@ char *l_strjoinv(char **str_array, const char delim);
 
 char **l_strv_new(void);
 void l_strv_free(char **str_array);
+DEFINE_CLEANUP_FUNC(l_strv_free);
 unsigned int l_strv_length(char **str_array);
 bool l_strv_contains(char **str_array, const char *item);
 char **l_strv_append(char **str_array, const char *str);
 char **l_strv_append_printf(char **str_array, const char *format, ...)
 					__attribute__((format(printf, 2, 3)));
 char **l_strv_append_vprintf(char **str_array, const char *format,
-							va_list args);
+							va_list args)
+					__attribute__((format(printf, 2, 0)));
 char **l_strv_copy(char **str_array);
+bool l_strv_eq(char **a, char **b);
 
 #ifdef __cplusplus
 }
diff --git a/ell/test.c b/ell/test.c
index cdae8df..e7aacf2 100644
--- a/ell/test.c
+++ b/ell/test.c
@@ -31,6 +31,7 @@
 #include "log.h"
 #include "test.h"
 #include "private.h"
+#include "useful.h"
 
 /**
  * SECTION:test
diff --git a/ell/timeout.c b/ell/timeout.c
index 1f2cddb..9f14e64 100644
--- a/ell/timeout.c
+++ b/ell/timeout.c
@@ -27,14 +27,16 @@
 #define _GNU_SOURCE
 #include <errno.h>
 #include <unistd.h>
+#include <stdbool.h>
 #include <string.h>
 #include <sys/epoll.h>
 #include <sys/timerfd.h>
 #include <time.h>
 #include <limits.h>
 
-#include "util.h"
+#include "useful.h"
 #include "timeout.h"
+#include "main-private.h"
 #include "private.h"
 
 /**
@@ -94,10 +96,10 @@ static inline int timeout_set(int fd, unsigned int seconds, long nanoseconds)
 	return timerfd_settime(fd, 0, &itimer, NULL);
 }
 
-static bool convert_ms(unsigned long milliseconds, unsigned int *seconds,
+static bool convert_ms(uint64_t milliseconds, unsigned int *seconds,
 			long *nanoseconds)
 {
-	unsigned long big_seconds = milliseconds / 1000;
+	uint64_t big_seconds = milliseconds / 1000;
 
 	if (big_seconds > UINT_MAX)
 		return false;
@@ -204,7 +206,7 @@ LIB_EXPORT struct l_timeout *l_timeout_create(unsigned int seconds,
  * Returns: a newly allocated #l_timeout object. On failure, the function
  * returns NULL.
  **/
-LIB_EXPORT struct l_timeout *l_timeout_create_ms(unsigned long milliseconds,
+LIB_EXPORT struct l_timeout *l_timeout_create_ms(uint64_t milliseconds,
 			l_timeout_notify_cb_t callback,
 			void *user_data, l_timeout_destroy_cb_t destroy)
 {
@@ -250,7 +252,7 @@ LIB_EXPORT void l_timeout_modify(struct l_timeout *timeout,
  * Modify an existing @timeout and rearm it.
  **/
 LIB_EXPORT void l_timeout_modify_ms(struct l_timeout *timeout,
-					unsigned long milliseconds)
+					uint64_t milliseconds)
 {
 	if (unlikely(!timeout))
 		return;
@@ -281,7 +283,7 @@ LIB_EXPORT void l_timeout_remove(struct l_timeout *timeout)
 	if (unlikely(!timeout))
 		return;
 
-	watch_remove(timeout->fd);
+	watch_remove(timeout->fd, false);
 
 	l_free(timeout);
 }
diff --git a/ell/timeout.h b/ell/timeout.h
index 889a287..09afb96 100644
--- a/ell/timeout.h
+++ b/ell/timeout.h
@@ -27,6 +27,8 @@
 extern "C" {
 #endif
 
+#include <stdint.h>
+
 struct l_timeout;
 
 typedef void (*l_timeout_notify_cb_t) (struct l_timeout *timeout,
@@ -36,13 +38,13 @@ typedef void (*l_timeout_destroy_cb_t) (void *user_data);
 struct l_timeout *l_timeout_create(unsigned int seconds,
 			l_timeout_notify_cb_t callback,
 			void *user_data, l_timeout_destroy_cb_t destroy);
-struct l_timeout *l_timeout_create_ms(unsigned long milliseconds,
+struct l_timeout *l_timeout_create_ms(uint64_t milliseconds,
 			l_timeout_notify_cb_t callback,
 			void *user_data, l_timeout_destroy_cb_t destroy);
 void l_timeout_modify(struct l_timeout *timeout,
 				unsigned int seconds);
 void l_timeout_modify_ms(struct l_timeout *timeout,
-				unsigned long milliseconds);
+				uint64_t milliseconds);
 void l_timeout_remove(struct l_timeout *timeout);
 void l_timeout_set_callback(struct l_timeout *timeout,
 				l_timeout_notify_cb_t callback, void *user_data,
diff --git a/ell/uintset.c b/ell/uintset.c
index b88e28e..863bc56 100644
--- a/ell/uintset.c
+++ b/ell/uintset.c
@@ -28,6 +28,7 @@
 #include <limits.h>
 
 #include "uintset.h"
+#include "useful.h"
 #include "private.h"
 
 #define BITS_PER_LONG (sizeof(unsigned long) * 8)
@@ -456,7 +457,7 @@ LIB_EXPORT uint32_t l_uintset_find_min(struct l_uintset *set)
  *
  * Call @function for every given number in @set.
  **/
-LIB_EXPORT void l_uintset_foreach(struct l_uintset *set,
+LIB_EXPORT void l_uintset_foreach(const struct l_uintset *set,
 					l_uintset_foreach_func_t function,
 					void *user_data)
 {
@@ -470,6 +471,30 @@ LIB_EXPORT void l_uintset_foreach(struct l_uintset *set,
 		function(set->min + bit, user_data);
 }
 
+/**
+ * l_uintset_clone:
+ * @original: The set of numbers
+ * @set_b: The set of numbers
+ *
+ * Returns: A newly allocated l_uintset object containing a copy of @original
+ **/
+LIB_EXPORT struct l_uintset *l_uintset_clone(const struct l_uintset *original)
+{
+	struct l_uintset *clone;
+	size_t bitmap_size;
+
+	if (unlikely(!original))
+		return NULL;
+
+	bitmap_size = sizeof(unsigned long) *
+		((original->size + BITS_PER_LONG - 1) / BITS_PER_LONG);
+
+	clone = l_uintset_new_from_range(original->min, original->max);
+	memcpy(clone->bits, original->bits, bitmap_size);
+
+	return clone;
+}
+
 /**
  * l_uintset_intersect:
  * @set_a: The set of numbers
@@ -530,3 +555,27 @@ LIB_EXPORT bool l_uintset_isempty(const struct l_uintset *set)
 
 	return true;
 }
+
+/**
+ * l_uintset_size
+ *
+ * @set: The set of numbers
+ *
+ * Returns the number of set elements
+ */
+LIB_EXPORT uint32_t l_uintset_size(const struct l_uintset *set)
+{
+	uint16_t i;
+	uint32_t offset_max;
+	uint32_t count = 0;
+
+	if (unlikely(!set))
+		return 0;
+
+	offset_max = (set->size + BITS_PER_LONG - 1) / BITS_PER_LONG;
+
+	for (i = 0; i < offset_max; i++)
+		count += __builtin_popcountl(set->bits[i]);
+
+	return count;
+}
diff --git a/ell/uintset.h b/ell/uintset.h
index 8215def..aa9de48 100644
--- a/ell/uintset.h
+++ b/ell/uintset.h
@@ -30,6 +30,7 @@ extern "C" {
 #include <stdint.h>
 #include <stddef.h>
 #include <stdbool.h>
+#include <ell/cleanup.h>
 
 typedef void (*l_uintset_foreach_func_t) (uint32_t number, void *user_data);
 
@@ -38,6 +39,7 @@ struct l_uintset;
 struct l_uintset *l_uintset_new_from_range(uint32_t min, uint32_t max);
 struct l_uintset *l_uintset_new(unsigned int size);
 void l_uintset_free(struct l_uintset *set);
+DEFINE_CLEANUP_FUNC(l_uintset_free);
 
 bool l_uintset_contains(struct l_uintset *set, uint32_t number);
 bool l_uintset_take(struct l_uintset *set, uint32_t number);
@@ -52,12 +54,14 @@ uint32_t l_uintset_find_min(struct l_uintset *set);
 uint32_t l_uintset_find_unused_min(struct l_uintset *set);
 uint32_t l_uintset_find_unused(struct l_uintset *set, uint32_t start);
 
-void l_uintset_foreach(struct l_uintset *set,
+void l_uintset_foreach(const struct l_uintset *set,
 			l_uintset_foreach_func_t function, void *user_data);
 
+struct l_uintset *l_uintset_clone(const struct l_uintset *original);
 struct l_uintset *l_uintset_intersect(const struct l_uintset *set_a,
 						const struct l_uintset *set_b);
 bool l_uintset_isempty(const struct l_uintset *set);
+uint32_t l_uintset_size(const struct l_uintset *set);
 
 #ifdef __cplusplus
 }
diff --git a/ell/useful.h b/ell/useful.h
new file mode 100644
index 0000000..4c8b23e
--- /dev/null
+++ b/ell/useful.h
@@ -0,0 +1,84 @@
+/*
+ *
+ *  Embedded Linux library
+ *
+ *  Copyright (C) 2021  Intel Corporation. All rights reserved.
+ *
+ *  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.1 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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define align_len(len, boundary) (((len)+(boundary)-1) & ~((boundary)-1))
+
+#define likely(x)   __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
+static inline size_t minsize(size_t a, size_t b)
+{
+	if (a <= b)
+		return a;
+
+	return b;
+}
+
+static inline void set_bit(void *addr, unsigned int bit)
+{
+	unsigned char *field = addr;
+	field[bit / 8] |= 1U << (bit % 8);
+}
+
+static inline int test_bit(const void *addr, unsigned int bit)
+{
+	const unsigned char *field = addr;
+	return (field[bit / 8] & (1U << (bit % 8))) != 0;
+}
+
+static inline unsigned char bit_field(const unsigned char oct,
+					unsigned int start, unsigned int n_bits)
+{
+	unsigned char mask = (1U << n_bits) - 1U;
+	return (oct >> start) & mask;
+}
+
+#define DIV_ROUND_CLOSEST(x, divisor)			\
+({							\
+	typeof(divisor) _d = (divisor);			\
+	typeof(x) _x = (x) + _d / 2;			\
+	_x / _d;					\
+})
+
+#define __AUTODESTRUCT(func)				\
+	__attribute((cleanup(func ## _cleanup)))
+
+#define _auto_(func)					\
+	__AUTODESTRUCT(func)
+
+/*
+ * Trick the compiler into thinking that var might be changed somehow by
+ * the asm
+ */
+#define DO_NOT_OPTIMIZE(var) \
+	__asm__ ("" : "=r" (var) : "0" (var));
+
+static inline int secure_select(int select_left, int l, int r)
+{
+	int mask = -(!!select_left);
+
+	return r ^ ((l ^ r) & mask);
+}
+
+#define struct_alloc(structname, ...) \
+	(struct structname *) l_memdup(&(struct structname) { __VA_ARGS__ }, \
+					sizeof(struct structname))
diff --git a/ell/utf8.c b/ell/utf8.c
index 44601dc..84ab774 100644
--- a/ell/utf8.c
+++ b/ell/utf8.c
@@ -27,10 +27,10 @@
 #include <stdio.h>
 #include <wchar.h>
 
-#include "util.h"
 #include "strv.h"
 #include "utf8.h"
 #include "private.h"
+#include "useful.h"
 
 /**
  * SECTION:utf8
diff --git a/ell/utf8.h b/ell/utf8.h
index c92da60..d1b1811 100644
--- a/ell/utf8.h
+++ b/ell/utf8.h
@@ -102,7 +102,7 @@ static inline __attribute__ ((always_inline)) bool l_ascii_isascii(int c)
 }
 
 bool l_utf8_validate(const char *src, size_t len, const char **end);
-size_t l_utf8_strlen(const char *src);
+size_t l_utf8_strlen(const char *str);
 
 int l_utf8_get_codepoint(const char *str, size_t len, wchar_t *cp);
 size_t l_utf8_from_wchar(wchar_t c, char *out_buf);
diff --git a/ell/util.c b/ell/util.c
index 26cea8a..9c9c476 100644
--- a/ell/util.c
+++ b/ell/util.c
@@ -26,12 +26,12 @@
 
 #define _GNU_SOURCE
 #include <stdio.h>
-#include <ctype.h>
-#include <stdarg.h>
 #include <stdlib.h>
 #include <limits.h>
 
+#include "utf8.h"
 #include "util.h"
+#include "useful.h"
 #include "private.h"
 
 /**
@@ -342,6 +342,19 @@ LIB_EXPORT bool l_str_has_suffix(const char *str, const char *suffix)
 	return !strcmp(&str[len_diff], suffix);
 }
 
+/**
+ * l_streq0:
+ * @a: First operand
+ * @b: Second operand
+ *
+ * Returns: True if @a and @b are both NULL or both non-NULL and identical
+ * according to strcmp.  False otherwise.
+ */
+LIB_EXPORT bool l_streq0(const char *a, const char *b)
+{
+	return a == b || (a && b && !strcmp(a, b));
+}
+
 static char *hexstring_common(const unsigned char *buf, size_t len,
 				const char hexdigits[static 16])
 {
@@ -372,7 +385,7 @@ static char *hexstring_common(const unsigned char *buf, size_t len,
  * lower case hex digits a-f.  If you require upper case hex digits, use
  * @l_util_hexstring_upper
  **/
-LIB_EXPORT char *l_util_hexstring(const unsigned char *buf, size_t len)
+LIB_EXPORT char *l_util_hexstring(const void *buf, size_t len)
 {
 	static const char hexdigits[] = "0123456789abcdef";
 	return hexstring_common(buf, len, hexdigits);
@@ -387,7 +400,7 @@ LIB_EXPORT char *l_util_hexstring(const unsigned char *buf, size_t len)
  * upper case hex digits a-f.  If you require lower case hex digits, use
  * @l_util_hexstring
  **/
-LIB_EXPORT char *l_util_hexstring_upper(const unsigned char *buf, size_t len)
+LIB_EXPORT char *l_util_hexstring_upper(const void *buf, size_t len)
 {
 	static const char hexdigits[] = "0123456789ABCDEF";
 	return hexstring_common(buf, len, hexdigits);
@@ -413,9 +426,10 @@ LIB_EXPORT unsigned char *l_util_from_hexstring(const char *str,
 		return NULL;
 
 	for (i = 0; str[i]; i++) {
-		c = toupper(str[i]);
+		c = str[i];
 
-		if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'))
+		if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') ||
+				(c >= 'a' && c <= 'f'))
 			continue;
 
 		return NULL;
@@ -431,21 +445,24 @@ LIB_EXPORT unsigned char *l_util_from_hexstring(const char *str,
 	buf = l_malloc(i >> 1);
 
 	for (i = 0, j = 0; i < len; i++, j++) {
-		c = toupper(str[i]);
+		c = str[i];
 
 		if (c >= '0' && c <= '9')
 			buf[j] = c - '0';
 		else if (c >= 'A' && c <= 'F')
 			buf[j] = 10 + c - 'A';
+		else if (c >= 'a' && c <= 'f')
+			buf[j] = 10 + c - 'a';
 
 		i += 1;
-
-		c = toupper(str[i]);
+		c = str[i];
 
 		if (c >= '0' && c <= '9')
 			buf[j] = buf[j] * 16 + c - '0';
 		else if (c >= 'A' && c <= 'F')
 			buf[j] = buf[j] * 16 + 10 + c - 'A';
+		else if (c >= 'a' && c <= 'f')
+			buf[j] = buf[j] * 16 + 10 + c - 'a';
 	}
 
 	if (out_len)
@@ -470,7 +487,7 @@ static void hexdump(const char dir, const unsigned char *buf, size_t len,
 		str[((i % 16) * 3) + 1] = ' ';
 		str[((i % 16) * 3) + 2] = hexdigits[buf[i] >> 4];
 		str[((i % 16) * 3) + 3] = hexdigits[buf[i] & 0xf];
-		str[(i % 16) + 51] = isprint(buf[i]) ? buf[i] : '.';
+		str[(i % 16) + 51] = l_ascii_isprint(buf[i]) ? buf[i] : '.';
 
 		if ((i + 1) % 16 == 0) {
 			str[49] = ' ';
@@ -528,6 +545,9 @@ LIB_EXPORT void l_util_hexdumpv(bool in, const struct iovec *iov,
 	size_t c;
 	const uint8_t *buf;
 
+	if (likely(!function))
+		return;
+
 	if (unlikely(!iov || !n_iov))
 		return;
 
@@ -549,7 +569,7 @@ LIB_EXPORT void l_util_hexdumpv(bool in, const struct iovec *iov,
 		str[((i % 16) * 3) + 1] = ' ';
 		str[((i % 16) * 3) + 2] = hexdigits[buf[c] >> 4];
 		str[((i % 16) * 3) + 3] = hexdigits[buf[c] & 0xf];
-		str[(i % 16) + 51] = isprint(buf[c]) ? buf[c] : '.';
+		str[(i % 16) + 51] = l_ascii_isprint(buf[c]) ? buf[c] : '.';
 
 		if ((i + 1) % 16 == 0) {
 			str[49] = ' ';
@@ -634,3 +654,34 @@ LIB_EXPORT const char *l_util_get_debugfs_path(void)
 
 	return path;
 }
+
+LIB_EXPORT bool l_memeq(const void *field, size_t size, uint8_t byte)
+{
+	const uint8_t *mem = field;
+	size_t i;
+
+	for (i = 0; i < size; i++)
+		if (mem[i] != byte)
+			return false;
+
+	return true;
+}
+
+__attribute__((noinline)) static int __secure_memeq(const void *field,
+						size_t size, uint8_t byte)
+{
+	unsigned int diff = 0;
+	size_t i;
+
+	for (i = 0; i < size; i++) {
+		diff |= ((uint8_t *) field)[i] ^ byte;
+		DO_NOT_OPTIMIZE(diff);
+	}
+
+	return diff;
+}
+
+LIB_EXPORT bool l_secure_memeq(const void *field, size_t size, uint8_t byte)
+{
+	return __secure_memeq(field, size, byte) == 0 ? true : false;
+}
diff --git a/ell/util.h b/ell/util.h
index 4f20ef0..9d4e36c 100644
--- a/ell/util.h
+++ b/ell/util.h
@@ -30,29 +30,30 @@
 #include <endian.h>
 #include <byteswap.h>
 #include <sys/uio.h>
+#include <ell/cleanup.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 #define l_container_of(ptr, type, member) ({				\
+_Pragma("GCC diagnostic push")						\
+_Pragma("GCC diagnostic ignored \"-Wcast-align\"")			\
 		const __typeof__(((type *) 0)->member) *__mptr = (ptr);	\
 		(type *)((char *) __mptr - offsetof(type, member));	\
+_Pragma("GCC diagnostic pop")						\
 	})
 
-#define likely(x)   __builtin_expect(!!(x), 1)
-#define unlikely(x) __builtin_expect(!!(x), 0)
-
 #define L_STRINGIFY(val) L_STRINGIFY_ARG(val)
 #define L_STRINGIFY_ARG(contents) #contents
 
 #define L_WARN_ON(condition) __extension__ ({				\
 		bool r = !!(condition);					\
-		if (unlikely(r))					\
+		if (r)							\
 			l_warn("WARNING: %s:%s() condition %s failed",	\
 				__FILE__, __func__,			\
 				#condition);				\
-		unlikely(r);						\
+		r;							\
 	})
 
 #define L_PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
@@ -226,9 +227,6 @@ static inline void l_put_be64(uint64_t val, void *ptr)
 	L_PUT_UNALIGNED(L_CPU_TO_BE64(val), (uint64_t *) ptr);
 }
 
-#define L_AUTO_CLEANUP_VAR(vartype,varname,destroy) \
-	vartype varname __attribute__((cleanup(destroy)))
-
 #define L_AUTO_FREE_VAR(vartype,varname) \
 	vartype varname __attribute__((cleanup(auto_free)))
 
@@ -238,6 +236,7 @@ void *l_malloc(size_t size) __attribute__ ((warn_unused_result, malloc));
 void *l_memdup(const void *mem, size_t size)
 			__attribute__ ((warn_unused_result, malloc));
 void l_free(void *ptr);
+DEFINE_CLEANUP_FUNC(l_free);
 
 void *l_realloc(void *mem, size_t size)
 			__attribute__ ((warn_unused_result, malloc));
@@ -248,13 +247,8 @@ static inline void auto_free(void *a)
 	l_free(*p);
 }
 
-static inline size_t minsize(size_t a, size_t b)
-{
-	if (a <= b)
-		return a;
-
-	return b;
-}
+#define l_steal_ptr(ptr) \
+	(__extension__ ({ typeof(ptr) _tmp = (ptr); (ptr) = NULL; _tmp; }))
 
 /**
  * l_new:
@@ -277,15 +271,17 @@ char *l_strdup(const char *str);
 char *l_strndup(const char *str, size_t max);
 char *l_strdup_printf(const char *format, ...)
 			__attribute__((format(printf, 1, 2)));
-char *l_strdup_vprintf(const char *format, va_list args);
+char *l_strdup_vprintf(const char *format, va_list args)
+			__attribute__((format(printf, 1, 0)));
 
 size_t l_strlcpy(char* dst, const char *src, size_t len);
 
 bool l_str_has_prefix(const char *str, const char *prefix);
 bool l_str_has_suffix(const char *str, const char *suffix);
+bool l_streq0(const char *a, const char *b);
 
-char *l_util_hexstring(const unsigned char *buf, size_t len);
-char *l_util_hexstring_upper(const unsigned char *buf, size_t len);
+char *l_util_hexstring(const void *buf, size_t len);
+char *l_util_hexstring_upper(const void *buf, size_t len);
 unsigned char *l_util_from_hexstring(const char *str, size_t *out_len);
 
 typedef void (*l_util_hexdump_func_t) (const char *str, void *user_data);
@@ -311,6 +307,124 @@ const char *l_util_get_debugfs_path(void);
        while (__result == -1L && errno == EINTR);  \
        __result; }))
 
+#define _L_IN_SET_CMP(val, type, cmp, ...) __extension__ ({		\
+		const type __v = (val);					\
+		const typeof(__v) __elems[] = {__VA_ARGS__};		\
+		unsigned int __i;					\
+		static const unsigned int __n = L_ARRAY_SIZE(__elems);	\
+		bool __r = false;					\
+		for (__i = 0; __i < __n && !__r; __i++)			\
+			__r = (cmp);					\
+		__r;							\
+	})
+
+/* Warning: evaluates all set elements even after @val has matched one */
+#define L_IN_SET(val, ...)	\
+	_L_IN_SET_CMP((val), __auto_type, __v == __elems[__i], ##__VA_ARGS__)
+
+#define L_IN_STRSET(val, ...)						\
+	_L_IN_SET_CMP((val), const char *, __v == __elems[__i] ||	\
+				(__v && __elems[__i] &&			\
+				 !strcmp(__v, __elems[__i])), ##__VA_ARGS__)
+
+/*
+ * Taken from https://github.com/chmike/cst_time_memcmp, adding a volatile to
+ * ensure the compiler does not try to optimize the constant time behavior.
+ * The code has been modified to add comments and project specific code
+ * styling.
+ * This specific piece of code is subject to the following copyright:
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015 Christophe Meessen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * This function performs a secure memory comparison of two buffers of size
+ * bytes, representing an integer (byte order is big endian). It returns
+ * a negative, zero or positif value if a < b, a == b or a > b respectively.
+ */
+static inline int l_secure_memcmp(const void *a, const void *b,
+					size_t size)
+{
+	const volatile uint8_t *aa = a;
+	const volatile uint8_t *bb = b;
+	int res = 0, diff, mask;
+
+	/*
+	 * We will compare all bytes, starting with the less significant. When
+	 * we find a non-zero difference, we update the result accordingly.
+	 */
+	if (size > 0) {
+		/*
+		 * The following couple of lines can be summarized as a
+		 * constant time/memory access version of:
+		 * if (diff != 0) res = diff;
+		 *
+		 * From the previous operation, we know that diff is in
+		 * [-255, 255]
+		 *
+		 * The following figure show the possible value of mask, based
+		 * on different cases of diff:
+		 *
+		 * diff  |   diff-1   |   ~diff    | ((diff-1) & ~diff) |  mask
+		 * ------|------------|------------|--------------------|------
+		 *   < 0 | 0xFFFFFFXX | 0x000000YY |     0x000000ZZ     |   0
+		 *  == 0 | 0xFFFFFFFF | 0xFFFFFFFF |     0xFFFFFFFF     | 0xF..F
+		 *  > 0  | 0x000000XX | 0xFFFFFFYY |     0x000000ZZ     |   0
+		 *
+		 * Hence, the mask allows to keep res when diff == 0, and to
+		 * set res to diff otherwise.
+		*/
+		do {
+			--size;
+			diff = aa[size] - bb[size];
+			mask = (((diff - 1) & ~diff) >> 8);
+			res = (res & mask) | diff;
+		} while (size != 0);
+	}
+
+	return res;
+}
+
+bool l_memeq(const void *field, size_t size, uint8_t byte);
+bool l_secure_memeq(const void *field, size_t size, uint8_t byte);
+
+static inline bool l_memeqzero(const void *field, size_t size)
+{
+	return l_memeq(field, size, 0);
+}
+
+static inline void l_secure_select(bool select_left,
+				const void *left, const void *right,
+				void *out, size_t len)
+{
+	const uint8_t *l = left;
+	const uint8_t *r = right;
+	uint8_t *o = out;
+	uint8_t mask = -(!!select_left);
+	size_t i;
+
+	for (i = 0; i < len; i++)
+		o[i] = r[i] ^ ((l[i] ^ r[i]) & mask);
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/ell/uuid.c b/ell/uuid.c
index de54015..8324ead 100644
--- a/ell/uuid.c
+++ b/ell/uuid.c
@@ -30,6 +30,7 @@
 #include "checksum.h"
 #include "random.h"
 #include "private.h"
+#include "useful.h"
 #include "uuid.h"
 
 const uint8_t L_UUID_NAMESPACE_DNS[16] = {
diff --git a/gatchat/gatmux.c b/gatchat/gatmux.c
index 4d89f03..95ffeb8 100644
--- a/gatchat/gatmux.c
+++ b/gatchat/gatmux.c
@@ -38,6 +38,7 @@
 #include "ringbuffer.h"
 #include "gatmux.h"
 #include "gsm0710.h"
+#include "src/missing.h"
 
 static const char *cmux_prefix[] = { "+CMUX:", NULL };
 static const char *none_prefix[] = { NULL };
@@ -908,7 +909,7 @@ static void mux_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
 	} else
 		goto error;
 
-	nmsd = g_memdup(msd, sizeof(struct mux_setup_data));
+	nmsd = g_memdup2(msd, sizeof(struct mux_setup_data));
 	g_at_chat_ref(nmsd->chat);
 
 	if (speed < 0)
diff --git a/gatchat/gatppp.c b/gatchat/gatppp.c
index 141e274..259e6d5 100644
--- a/gatchat/gatppp.c
+++ b/gatchat/gatppp.c
@@ -806,6 +806,11 @@ void g_at_ppp_set_server_info(GAtPPP *ppp, const char *remote,
 	ipcp_set_server_info(ppp->ipcp, r, d1, d2);
 }
 
+void g_at_ppp_set_accm(GAtPPP *ppp, guint32 accm)
+{
+	lcp_set_accm(ppp->lcp, accm);
+}
+
 void g_at_ppp_set_acfc_enabled(GAtPPP *ppp, gboolean enabled)
 {
 	lcp_set_acfc_enabled(ppp->lcp, enabled);
diff --git a/gatchat/gatppp.h b/gatchat/gatppp.h
index dd203c2..a12e42e 100644
--- a/gatchat/gatppp.h
+++ b/gatchat/gatppp.h
@@ -88,6 +88,7 @@ void g_at_ppp_set_recording(GAtPPP *ppp, const char *filename);
 void g_at_ppp_set_server_info(GAtPPP *ppp, const char *remote_ip,
 				const char *dns1, const char *dns2);
 
+void g_at_ppp_set_accm(GAtPPP *ppp, guint32 accm);
 void g_at_ppp_set_acfc_enabled(GAtPPP *ppp, gboolean enabled);
 void g_at_ppp_set_pfc_enabled(GAtPPP *ppp, gboolean enabled);
 
diff --git a/gatchat/gatresult.c b/gatchat/gatresult.c
index 883b410..1f9e2a6 100644
--- a/gatchat/gatresult.c
+++ b/gatchat/gatresult.c
@@ -231,6 +231,7 @@ gboolean g_at_result_iter_next_hexstring(GAtResultIter *iter,
 	if (line[pos] == ',') {
 		end = pos;
 		iter->buf[pos] = '\0';
+		*length = 0;
 		goto out;
 	}
 
diff --git a/gatchat/gatresult.h b/gatchat/gatresult.h
index e92d38b..5993b23 100644
--- a/gatchat/gatresult.h
+++ b/gatchat/gatresult.h
@@ -35,7 +35,7 @@ struct _GAtResult {
 
 typedef struct _GAtResult GAtResult;
 
-#define G_AT_RESULT_LINE_LENGTH_MAX 2048
+#define G_AT_RESULT_LINE_LENGTH_MAX 4096
 
 struct _GAtResultIter {
 	GAtResult *result;
diff --git a/gatchat/gsmdial.c b/gatchat/gsmdial.c
index 60e4f24..09dd850 100644
--- a/gatchat/gsmdial.c
+++ b/gatchat/gsmdial.c
@@ -53,6 +53,7 @@ static gint option_cid = 0;
 static gchar *option_apn = NULL;
 static gint option_offmode = 0;
 static gboolean option_legacy = FALSE;
+static gchar *option_auth_method;
 static gchar *option_username = NULL;
 static gchar *option_password = NULL;
 static gchar *option_pppdump = NULL;
@@ -369,6 +370,11 @@ static void connect_cb(gboolean ok, GAtResult *result, gpointer user_data)
 	}
 	g_at_ppp_set_debug(ppp, gsmdial_debug, "PPP");
 
+	if (option_auth_method && strcmp(option_auth_method, "PAP") == 0)
+		g_at_ppp_set_auth_method(ppp, G_AT_PPP_AUTH_METHOD_PAP);
+	else if (option_auth_method && strcmp(option_auth_method, "NONE") == 0)
+		g_at_ppp_set_auth_method(ppp, G_AT_PPP_AUTH_METHOD_NONE);
+
 	g_at_ppp_set_credentials(ppp, option_username, option_password);
 
 	g_at_ppp_set_acfc_enabled(ppp, option_acfc);
@@ -677,6 +683,10 @@ static GOptionEntry options[] = {
 				"Use ATD*99***<cid>#" },
 	{ "bluetooth", 'b', 0, G_OPTION_ARG_NONE, &option_bluetooth,
 				"Use only ATD*99" },
+	{ "auth", 'A', 0, G_OPTION_ARG_STRING, &option_auth_method,
+				"Specify the authentication method for the PPP"
+				" connection: CHAP, PAP or NONE. CHAP is used"
+				" by default." },
 	{ "username", 'u', 0, G_OPTION_ARG_STRING, &option_username,
 				"Specify PPP username" },
 	{ "password", 'w', 0, G_OPTION_ARG_STRING, &option_password,
diff --git a/gatchat/ppp.h b/gatchat/ppp.h
index ac1a7ef..6c02b05 100644
--- a/gatchat/ppp.h
+++ b/gatchat/ppp.h
@@ -90,6 +90,7 @@ static inline void __put_unaligned_short(void *p, guint16 val)
 struct pppcp_data *lcp_new(GAtPPP *ppp, gboolean dormant);
 void lcp_free(struct pppcp_data *lcp);
 void lcp_protocol_reject(struct pppcp_data *lcp, guint8 *packet, gsize len);
+void lcp_set_accm(struct pppcp_data *pppcp, guint32 accm);
 void lcp_set_acfc_enabled(struct pppcp_data *pppcp, gboolean enabled);
 void lcp_set_pfc_enabled(struct pppcp_data *pppcp, gboolean enabled);
 
diff --git a/gatchat/ppp_ipcp.c b/gatchat/ppp_ipcp.c
index 125a542..4d4dd37 100644
--- a/gatchat/ppp_ipcp.c
+++ b/gatchat/ppp_ipcp.c
@@ -34,6 +34,7 @@
 #include "gatutil.h"
 #include "gatppp.h"
 #include "ppp.h"
+#include "src/missing.h"
 
 #define IPCP_SUPPORTED_CODES	((1 << PPPCP_CODE_TYPE_CONFIGURE_REQUEST) | \
 				(1 << PPPCP_CODE_TYPE_CONFIGURE_ACK) | \
@@ -371,7 +372,7 @@ static enum rcr_result ipcp_server_rcr(struct ipcp_data *ipcp,
 
 	if (len > 0) {
 		*new_len = len;
-		*new_options = g_memdup(nak_options, len);
+		*new_options = g_memdup2(nak_options, len);
 
 		return RCR_NAK;
 	}
diff --git a/gatchat/ppp_ipv6cp.c b/gatchat/ppp_ipv6cp.c
index 94feacc..cde4020 100644
--- a/gatchat/ppp_ipv6cp.c
+++ b/gatchat/ppp_ipv6cp.c
@@ -34,6 +34,7 @@
 
 #include "gatppp.h"
 #include "ppp.h"
+#include "missing.h"
 
 #define IPV6CP_SUPPORTED_CODES	((1 << PPPCP_CODE_TYPE_CONFIGURE_REQUEST) | \
 				(1 << PPPCP_CODE_TYPE_CONFIGURE_ACK) | \
@@ -160,7 +161,7 @@ static enum rcr_result ipv6cp_server_rcr(struct ipv6cp_data *ipv6cp,
 
 	if (len > 0) {
 		*new_len = len;
-		*new_options = g_memdup(nak_options, len);
+		*new_options = g_memdup2(nak_options, len);
 
 		return RCR_NAK;
 	}
diff --git a/gatchat/ppp_lcp.c b/gatchat/ppp_lcp.c
index 3fe3821..7c45a27 100644
--- a/gatchat/ppp_lcp.c
+++ b/gatchat/ppp_lcp.c
@@ -121,7 +121,9 @@ static void lcp_generate_config_options(struct lcp_data *lcp)
 
 static void lcp_reset_config_options(struct lcp_data *lcp)
 {
-	/* Using the default ACCM */
+	/* Using RX ACCM = 0 instead of the default ACCM */
+	lcp->accm = 0;
+	lcp->req_options |= REQ_OPTION_ACCM;
 
 	lcp_generate_config_options(lcp);
 }
@@ -398,6 +400,17 @@ struct pppcp_data *lcp_new(GAtPPP *ppp, gboolean is_server)
 	return pppcp;
 }
 
+void lcp_set_accm(struct pppcp_data *pppcp, guint32 accm)
+{
+	struct lcp_data *lcp = pppcp_get_data(pppcp);
+
+	lcp->accm = accm;
+	lcp->req_options |= REQ_OPTION_ACCM;
+
+	lcp_generate_config_options(lcp);
+	pppcp_set_local_options(pppcp, lcp->options, lcp->options_len);
+}
+
 void lcp_set_acfc_enabled(struct pppcp_data *pppcp, gboolean enabled)
 {
 	struct lcp_data *lcp = pppcp_get_data(pppcp);
diff --git a/include/netmon.h b/include/netmon.h
index a99d6ca..53f9d39 100644
--- a/include/netmon.h
+++ b/include/netmon.h
@@ -72,6 +72,9 @@ enum ofono_netmon_info {
 	OFONO_NETMON_INFO_EARFCN, /* int */
 	OFONO_NETMON_INFO_EBAND, /* int */
 	OFONO_NETMON_INFO_CQI, /* int */
+	OFONO_NETMON_INFO_PCI, /* int */
+	OFONO_NETMON_INFO_TAC, /* int */
+	OFONO_NETMON_INFO_SNR, /* int */
 	OFONO_NETMON_INFO_INVALID,
 };
 
diff --git a/include/radio-settings.h b/include/radio-settings.h
index 0836d8b..638452f 100644
--- a/include/radio-settings.h
+++ b/include/radio-settings.h
@@ -60,8 +60,7 @@ typedef void (*ofono_radio_settings_rat_mode_set_cb_t)(
 						void *data);
 typedef void (*ofono_radio_settings_rat_mode_query_cb_t)(
 					const struct ofono_error *error,
-					enum ofono_radio_access_mode mode,
-					void *data);
+					int mode, void *data);
 
 typedef void (*ofono_radio_settings_band_set_cb_t)(
 						const struct ofono_error *error,
@@ -93,8 +92,7 @@ struct ofono_radio_settings_driver {
 	void (*query_rat_mode)(struct ofono_radio_settings *rs,
 				ofono_radio_settings_rat_mode_query_cb_t cb,
 				void *data);
-	void (*set_rat_mode)(struct ofono_radio_settings *rs,
-				enum ofono_radio_access_mode mode,
+	void (*set_rat_mode)(struct ofono_radio_settings *rs, unsigned int mode,
 				ofono_radio_settings_rat_mode_set_cb_t cb,
 				void *data);
 	void (*query_band)(struct ofono_radio_settings *rs,
diff --git a/install-sh b/install-sh
index 8175c64..ec298b5 100755
--- a/install-sh
+++ b/install-sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 # install - install a program, script, or datafile
 
-scriptversion=2018-03-11.20; # UTC
+scriptversion=2020-11-14.01; # UTC
 
 # This originates from X11R5 (mit/util/scripts/install.sh), which was
 # later released in X11R6 (xc/config/util/install.sh) with the
@@ -69,6 +69,11 @@ posix_mkdir=
 # Desired mode of installed file.
 mode=0755
 
+# Create dirs (including intermediate dirs) using mode 755.
+# This is like GNU 'install' as of coreutils 8.32 (2020).
+mkdir_umask=22
+
+backupsuffix=
 chgrpcmd=
 chmodcmd=$chmodprog
 chowncmd=
@@ -99,18 +104,28 @@ Options:
      --version  display version info and exit.
 
   -c            (ignored)
-  -C            install only if different (preserve the last data modification time)
+  -C            install only if different (preserve data modification time)
   -d            create directories instead of installing files.
   -g GROUP      $chgrpprog installed files to GROUP.
   -m MODE       $chmodprog installed files to MODE.
   -o USER       $chownprog installed files to USER.
+  -p            pass -p to $cpprog.
   -s            $stripprog installed files.
+  -S SUFFIX     attempt to back up existing files, with suffix SUFFIX.
   -t DIRECTORY  install into DIRECTORY.
   -T            report an error if DSTFILE is a directory.
 
 Environment variables override the default commands:
   CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
   RMPROG STRIPPROG
+
+By default, rm is invoked with -f; when overridden with RMPROG,
+it's up to you to specify -f if you want it.
+
+If -S is not specified, no backups are attempted.
+
+Email bug reports to bug-automake@gnu.org.
+Automake home page: https://www.gnu.org/software/automake/
 "
 
 while test $# -ne 0; do
@@ -137,8 +152,13 @@ while test $# -ne 0; do
     -o) chowncmd="$chownprog $2"
         shift;;
 
+    -p) cpprog="$cpprog -p";;
+
     -s) stripcmd=$stripprog;;
 
+    -S) backupsuffix="$2"
+        shift;;
+
     -t)
         is_target_a_directory=always
         dst_arg=$2
@@ -255,6 +275,10 @@ do
     dstdir=$dst
     test -d "$dstdir"
     dstdir_status=$?
+    # Don't chown directories that already exist.
+    if test $dstdir_status = 0; then
+      chowncmd=""
+    fi
   else
 
     # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
@@ -301,22 +325,6 @@ do
   if test $dstdir_status != 0; then
     case $posix_mkdir in
       '')
-        # Create intermediate dirs using mode 755 as modified by the umask.
-        # This is like FreeBSD 'install' as of 1997-10-28.
-        umask=`umask`
-        case $stripcmd.$umask in
-          # Optimize common cases.
-          *[2367][2367]) mkdir_umask=$umask;;
-          .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
-
-          *[0-7])
-            mkdir_umask=`expr $umask + 22 \
-              - $umask % 100 % 40 + $umask % 20 \
-              - $umask % 10 % 4 + $umask % 2
-            `;;
-          *) mkdir_umask=$umask,go-w;;
-        esac
-
         # With -d, create the new directory with the user-specified mode.
         # Otherwise, rely on $mkdir_umask.
         if test -n "$dir_arg"; then
@@ -326,52 +334,49 @@ do
         fi
 
         posix_mkdir=false
-        case $umask in
-          *[123567][0-7][0-7])
-            # POSIX mkdir -p sets u+wx bits regardless of umask, which
-            # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
-            ;;
-          *)
-            # Note that $RANDOM variable is not portable (e.g. dash);  Use it
-            # here however when possible just to lower collision chance.
-            tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
-
-            trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
-
-            # Because "mkdir -p" follows existing symlinks and we likely work
-            # directly in world-writeable /tmp, make sure that the '$tmpdir'
-            # directory is successfully created first before we actually test
-            # 'mkdir -p' feature.
-            if (umask $mkdir_umask &&
-                $mkdirprog $mkdir_mode "$tmpdir" &&
-                exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
-            then
-              if test -z "$dir_arg" || {
-                   # Check for POSIX incompatibilities with -m.
-                   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
-                   # other-writable bit of parent directory when it shouldn't.
-                   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
-                   test_tmpdir="$tmpdir/a"
-                   ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
-                   case $ls_ld_tmpdir in
-                     d????-?r-*) different_mode=700;;
-                     d????-?--*) different_mode=755;;
-                     *) false;;
-                   esac &&
-                   $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
-                     ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
-                     test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
-                   }
-                 }
-              then posix_mkdir=:
-              fi
-              rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
-            else
-              # Remove any dirs left behind by ancient mkdir implementations.
-              rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
-            fi
-            trap '' 0;;
-        esac;;
+	# The $RANDOM variable is not portable (e.g., dash).  Use it
+	# here however when possible just to lower collision chance.
+	tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+
+	trap '
+	  ret=$?
+	  rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null
+	  exit $ret
+	' 0
+
+	# Because "mkdir -p" follows existing symlinks and we likely work
+	# directly in world-writeable /tmp, make sure that the '$tmpdir'
+	# directory is successfully created first before we actually test
+	# 'mkdir -p'.
+	if (umask $mkdir_umask &&
+	    $mkdirprog $mkdir_mode "$tmpdir" &&
+	    exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
+	then
+	  if test -z "$dir_arg" || {
+	       # Check for POSIX incompatibilities with -m.
+	       # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+	       # other-writable bit of parent directory when it shouldn't.
+	       # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+	       test_tmpdir="$tmpdir/a"
+	       ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
+	       case $ls_ld_tmpdir in
+		 d????-?r-*) different_mode=700;;
+		 d????-?--*) different_mode=755;;
+		 *) false;;
+	       esac &&
+	       $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
+		 ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
+		 test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+	       }
+	     }
+	  then posix_mkdir=:
+	  fi
+	  rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
+	else
+	  # Remove any dirs left behind by ancient mkdir implementations.
+	  rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
+	fi
+	trap '' 0;;
     esac
 
     if
@@ -382,7 +387,7 @@ do
     then :
     else
 
-      # The umask is ridiculous, or mkdir does not conform to POSIX,
+      # mkdir does not conform to POSIX,
       # or it failed possibly due to a race condition.  Create the
       # directory the slow way, step by step, checking for races as we go.
 
@@ -411,7 +416,7 @@ do
           prefixes=
         else
           if $posix_mkdir; then
-            (umask=$mkdir_umask &&
+            (umask $mkdir_umask &&
              $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
             # Don't fail if two instances are running concurrently.
             test -d "$prefix" || exit 1
@@ -451,7 +456,18 @@ do
     trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
 
     # Copy the file name to the temp name.
-    (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+    (umask $cp_umask &&
+     { test -z "$stripcmd" || {
+	 # Create $dsttmp read-write so that cp doesn't create it read-only,
+	 # which would cause strip to fail.
+	 if test -z "$doit"; then
+	   : >"$dsttmp" # No need to fork-exec 'touch'.
+	 else
+	   $doit touch "$dsttmp"
+	 fi
+       }
+     } &&
+     $doit_exec $cpprog "$src" "$dsttmp") &&
 
     # and set any options; do chmod last to preserve setuid bits.
     #
@@ -477,6 +493,13 @@ do
     then
       rm -f "$dsttmp"
     else
+      # If $backupsuffix is set, and the file being installed
+      # already exists, attempt a backup.  Don't worry if it fails,
+      # e.g., if mv doesn't support -f.
+      if test -n "$backupsuffix" && test -f "$dst"; then
+        $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null
+      fi
+
       # Rename the file to the real destination.
       $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
 
@@ -491,9 +514,9 @@ do
         # file should still install successfully.
         {
           test ! -f "$dst" ||
-          $doit $rmcmd -f "$dst" 2>/dev/null ||
+          $doit $rmcmd "$dst" 2>/dev/null ||
           { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
-            { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+            { $doit $rmcmd "$rmtmp" 2>/dev/null; :; }
           } ||
           { echo "$0: cannot unlink or rename $dst" >&2
             (exit 1); exit 1
diff --git a/ltmain.sh b/ltmain.sh
old mode 100644
new mode 100755
index d11f1e0..21e5e07
--- a/ltmain.sh
+++ b/ltmain.sh
@@ -31,7 +31,7 @@
 
 PROGRAM=libtool
 PACKAGE=libtool
-VERSION="2.4.6 Debian-2.4.6-11"
+VERSION="2.4.6 Debian-2.4.6-15"
 package_revision=2.4.6
 
 
@@ -387,7 +387,7 @@ EXIT_SKIP=77	  # $? = 77 is used to indicate a skipped test to automake.
 # putting '$debug_cmd' at the start of all your functions, you can get
 # bash to show function call trace with:
 #
-#    debug_cmd='eval echo "${FUNCNAME[0]} $*" >&2' bash your-script-name
+#    debug_cmd='echo "${FUNCNAME[0]} $*" >&2' bash your-script-name
 debug_cmd=${debug_cmd-":"}
 exit_cmd=:
 
@@ -2141,7 +2141,7 @@ include the following information:
        compiler:       $LTCC
        compiler flags: $LTCFLAGS
        linker:         $LD (gnu? $with_gnu_ld)
-       version:        $progname $scriptversion Debian-2.4.6-11
+       version:        $progname $scriptversion Debian-2.4.6-15
        automake:       `($AUTOMAKE --version) 2>/dev/null |$SED 1q`
        autoconf:       `($AUTOCONF --version) 2>/dev/null |$SED 1q`
 
@@ -7368,10 +7368,12 @@ func_mode_link ()
       # -stdlib=*            select c++ std lib with clang
       # -fsanitize=*         Clang/GCC memory and address sanitizer
       # -fuse-ld=*           Linker select flags for GCC
+      # -static-*            direct GCC to link specific libraries statically
+      # -fcilkplus           Cilk Plus language extension features for C/C++
       -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
       -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
       -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \
-      -specs=*|-fsanitize=*|-fuse-ld=*)
+      -specs=*|-fsanitize=*|-fuse-ld=*|-static-*|-fcilkplus)
         func_quote_for_eval "$arg"
 	arg=$func_quote_for_eval_result
         func_append compile_command " $arg"
diff --git a/missing b/missing
index 625aeb1..8d0eaad 100755
--- a/missing
+++ b/missing
@@ -3,7 +3,7 @@
 
 scriptversion=2018-03-07.03; # UTC
 
-# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
 # Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
 
 # This program is free software; you can redistribute it and/or modify
diff --git a/plugins/allowed-apns.c b/plugins/allowed-apns.c
index b222b91..199202b 100644
--- a/plugins/allowed-apns.c
+++ b/plugins/allowed-apns.c
@@ -52,6 +52,7 @@ struct allowed_apns_ctx {
 	struct ofono_sim_context *sim_context;
 	DBusMessage *pending;
 	DBusMessage *reply;
+	bool registered;
 };
 
 static void context_destroy(gpointer data)
@@ -162,6 +163,9 @@ static void sim_state_watch(enum ofono_sim_state new_state, void *data)
 	DBusConnection *conn = ofono_dbus_get_connection();
 
 	if (new_state != OFONO_SIM_STATE_READY) {
+		if (!ctx->registered)
+			return;
+
 		g_dbus_unregister_interface(conn,
 				ofono_modem_get_path(ctx->modem),
 				ALLOWED_ACCESS_POINTS_INTERFACE);
@@ -169,6 +173,7 @@ static void sim_state_watch(enum ofono_sim_state new_state, void *data)
 		ofono_modem_remove_interface(ctx->modem,
 				ALLOWED_ACCESS_POINTS_INTERFACE);
 
+		ctx->registered = false;
 		return;
 	}
 
@@ -183,6 +188,7 @@ static void sim_state_watch(enum ofono_sim_state new_state, void *data)
 		return;
 	}
 
+	ctx->registered = true;
 	ofono_modem_add_interface(ctx->modem,
 			ALLOWED_ACCESS_POINTS_INTERFACE);
 }
diff --git a/plugins/droid.c b/plugins/droid.c
new file mode 100644
index 0000000..220d440
--- /dev/null
+++ b/plugins/droid.c
@@ -0,0 +1,206 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2009  Collabora Ltd. All rights reserved.
+ *  Copyright (C) 2020  Pavel Machek. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gatchat.h>
+#include <gattty.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-barring.h>
+#include <ofono/call-forwarding.h>
+#include <ofono/call-meter.h>
+#include <ofono/call-settings.h>
+#include <ofono/devinfo.h>
+#include <ofono/message-waiting.h>
+#include <ofono/netreg.h>
+#include <ofono/phonebook.h>
+#include <ofono/sim.h>
+#include <ofono/sms.h>
+#include <ofono/ussd.h>
+#include <ofono/voicecall.h>
+
+#include <drivers/atmodem/atutil.h>
+#include <drivers/atmodem/vendor.h>
+
+static void droid_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	ofono_info("%s%s", prefix, str);
+}
+
+/* Detect hardware, and initialize if found */
+static int droid_probe(struct ofono_modem *modem)
+{
+	DBG("");
+
+	return 0;
+}
+
+static void droid_remove(struct ofono_modem *modem)
+{
+	GAtChat *chat = ofono_modem_get_data(modem);
+
+	DBG("");
+
+	if (chat) {
+		g_at_chat_unref(chat);
+		ofono_modem_set_data(modem, NULL);
+	}
+}
+
+static void cfun_set_on_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+
+	DBG("");
+
+	if (ok)
+		ofono_modem_set_powered(modem, TRUE);
+}
+
+/* power up hardware */
+static int droid_enable(struct ofono_modem *modem)
+{
+	GAtChat *chat;
+
+	DBG("");
+
+	chat = at_util_open_device(modem, "Device", droid_debug, "", NULL);
+	ofono_modem_set_data(modem, chat);
+
+	/* ensure modem is in a known state; verbose on, echo/quiet off */
+	g_at_chat_send(chat, "ATE0Q0V1", NULL, NULL, NULL, NULL);
+
+	/* power up modem */
+	g_at_chat_send(chat, "AT+CFUN=1", NULL, cfun_set_on_cb, modem, NULL);
+
+	return 0;
+}
+
+static void cfun_set_off_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	GAtChat *chat = ofono_modem_get_data(modem);
+
+	DBG("");
+
+	g_at_chat_unref(chat);
+	ofono_modem_set_data(modem, NULL);
+
+	if (ok)
+		ofono_modem_set_powered(modem, FALSE);
+}
+
+static int droid_disable(struct ofono_modem *modem)
+{
+	GAtChat *chat = ofono_modem_get_data(modem);
+
+	DBG("");
+
+	/* power down modem */
+	g_at_chat_cancel_all(chat);
+	g_at_chat_unregister_all(chat);
+	g_at_chat_send(chat, "AT+CFUN=0", NULL, cfun_set_off_cb, modem, NULL);
+
+	return -EINPROGRESS;
+}
+
+static void droid_pre_sim(struct ofono_modem *modem)
+{
+	GAtChat *chat = ofono_modem_get_data(modem);
+	struct ofono_sim *sim;
+
+	DBG("");
+
+	ofono_devinfo_create(modem, 0, "atmodem", chat);
+	sim = ofono_sim_create(modem, OFONO_VENDOR_DROID, "atmodem", chat);
+	ofono_voicecall_create(modem, OFONO_VENDOR_DROID, "atmodem", chat);
+
+	if (sim)
+		ofono_sim_inserted_notify(sim, TRUE);
+}
+
+static void droid_post_sim(struct ofono_modem *modem)
+{
+	GAtChat *chat = ofono_modem_get_data(modem);
+	struct ofono_message_waiting *mw;
+
+	DBG("");
+
+	ofono_ussd_create(modem, 0, "atmodem", chat);
+	ofono_call_forwarding_create(modem, 0, "atmodem", chat);
+	ofono_call_settings_create(modem, 0, "atmodem", chat);
+	ofono_netreg_create(modem, 0, "atmodem", chat);
+	/*
+	 * Droid 4 modem has problems with AT+CPUC?, avoid call meter for now.
+	 */
+	ofono_call_barring_create(modem, 0, "atmodem", chat);
+	ofono_sms_create(modem, OFONO_VENDOR_DROID, "atmodem", chat);
+	ofono_phonebook_create(modem, 0, "atmodem", chat);
+
+	mw = ofono_message_waiting_create(modem);
+	if (mw)
+		ofono_message_waiting_register(mw);
+}
+
+static struct ofono_modem_driver droid_driver = {
+	.name		= "droid",
+	.probe		= droid_probe,
+	.remove		= droid_remove,
+	.enable		= droid_enable,
+	.disable	= droid_disable,
+	.pre_sim	= droid_pre_sim,
+	.post_sim	= droid_post_sim,
+};
+
+static int droid_init(void)
+{
+	return ofono_modem_driver_register(&droid_driver);
+}
+
+static void droid_exit(void)
+{
+	ofono_modem_driver_unregister(&droid_driver);
+}
+
+/* Modem in Motorola Droid has few different interfaces:
+ * -- gsmmux over serial -- using very non-standard commands
+ * -- QMI -- unfortunately not usable without gsmmux
+ * -- standard AT over ttyUSB4 -- unfortunately quite broken
+ *
+ * This driver is for the standard AT commands.
+ */
+
+OFONO_PLUGIN_DEFINE(droid, "Motorola Droid modem driver", VERSION,
+			OFONO_PLUGIN_PRIORITY_DEFAULT, droid_init, droid_exit)
diff --git a/plugins/gemalto.c b/plugins/gemalto.c
index 297aaea..d1962ab 100644
--- a/plugins/gemalto.c
+++ b/plugins/gemalto.c
@@ -48,6 +48,8 @@
 #include <ofono/gprs.h>
 #include <ofono/gprs-context.h>
 #include <ofono/location-reporting.h>
+#include <ofono/netmon.h>
+#include <ofono/radio-settings.h>
 
 #include <drivers/atmodem/atutil.h>
 #include <drivers/atmodem/vendor.h>
@@ -58,6 +60,8 @@
 #define GEMALTO_MODEL_PHS8P	"0053"
 /* ALS3, PLS8-E, and PLS8-X family */
 #define GEMALTO_MODEL_ALS3_PLS8x	"0061"
+/* ELS81 modem */
+#define GEMALTO_MODEL_ELS81x   "005b"
 
 static const char *none_prefix[] = { NULL };
 static const char *sctm_prefix[] = { "^SCTM:", NULL };
@@ -586,22 +590,32 @@ static void gemalto_post_sim(struct ofono_modem *modem)
 	struct ofono_gprs *gprs;
 	struct ofono_gprs_context *gc;
 	const char *model = ofono_modem_get_string(modem, "Model");
+	const char *driver = NULL;
+	const char *iface = NULL;
 
 	DBG("%p", modem);
 
 	ofono_phonebook_create(modem, 0, "atmodem", data->app);
 
 	ofono_sms_create(modem, OFONO_VENDOR_GEMALTO, "atmodem", data->app);
+	ofono_radio_settings_create(modem, 0, "gemaltomodem", data->app);
 
 	gprs = ofono_gprs_create(modem, 0, "atmodem", data->app);
-	gc = ofono_gprs_context_create(modem, 0, "atmodem", data->mdm);
+
+	iface = ofono_modem_get_string(modem, "NetworkInterface");
+	if (iface) {
+		driver = "gemaltomodem";
+	} else {
+		driver = "atmodem";
+	}
+
+	gc = ofono_gprs_context_create(modem, 0, driver, data->app);
 
 	if (gprs && gc)
 		ofono_gprs_add_context(gprs, gc);
 
-	ofono_ussd_create(modem, 0, "atmodem", data->app);
-
-	if (!g_strcmp0(model, GEMALTO_MODEL_ALS3_PLS8x))
+	if (!g_strcmp0(model, GEMALTO_MODEL_ALS3_PLS8x) ||
+	    !g_strcmp0(model, GEMALTO_MODEL_ELS81x))
 		ofono_lte_create(modem, OFONO_VENDOR_GEMALTO,
 						"atmodem", data->app);
 }
@@ -609,6 +623,7 @@ static void gemalto_post_sim(struct ofono_modem *modem)
 static void gemalto_post_online(struct ofono_modem *modem)
 {
 	struct gemalto_data *data = ofono_modem_get_data(modem);
+	const char *model = ofono_modem_get_string(modem, "Model");
 
 	DBG("%p", modem);
 
@@ -621,6 +636,12 @@ static void gemalto_post_online(struct ofono_modem *modem)
 	ofono_call_settings_create(modem, 0, "atmodem", data->app);
 	ofono_call_meter_create(modem, 0, "atmodem", data->app);
 	ofono_call_barring_create(modem, 0, "atmodem", data->app);
+
+	ofono_ussd_create(modem, 0, "atmodem", data->app);
+
+	if (!g_strcmp0(model, GEMALTO_MODEL_ELS81x))
+		ofono_netmon_create(modem, OFONO_VENDOR_GEMALTO,
+					"gemaltomodem", data->app);
 }
 
 static struct ofono_modem_driver gemalto_driver = {
diff --git a/plugins/huawei.c b/plugins/huawei.c
index bdf7bc3..c524cdd 100644
--- a/plugins/huawei.c
+++ b/plugins/huawei.c
@@ -419,6 +419,10 @@ static void sysinfo_enable_cb(gboolean ok, GAtResult *result,
 	g_at_chat_send(data->modem, "AT&C0", NULL, NULL, NULL, NULL);
 	g_at_chat_send(data->pcui, "AT&C0", NULL, NULL, NULL, NULL);
 
+	/* Restore settings after restart */
+	g_at_chat_send(data->modem, "AT&F0", NULL, NULL, NULL, NULL);
+	g_at_chat_send(data->pcui, "AT&F0", NULL, NULL, NULL, NULL);
+
 	/*
 	 * Ensure that the modem is using GSM character set and not IRA,
 	 * otherwise weirdness with umlauts and other non-ASCII characters
@@ -583,9 +587,6 @@ static void modem_disconnect(gpointer user_data)
 	g_at_chat_unref(data->modem);
 	data->modem = NULL;
 
-	/* close gprs context driver */
-	ofono_gprs_context_remove(data->gc);
-
 	/* reopen modem channel */
 	data->modem = open_device(modem, "Modem", "Modem: ");
 
@@ -594,6 +595,10 @@ static void modem_disconnect(gpointer user_data)
 		return;
 	}
 
+	/* close previous gprs context driver */
+	if (data->gc)
+		ofono_gprs_context_remove(data->gc);
+
 	/* configure modem channel */
 	g_at_chat_set_disconnect_function(data->modem, modem_disconnect, modem);
 	g_at_chat_set_slave(data->modem, data->pcui);
diff --git a/plugins/quectel.c b/plugins/quectel.c
index 5d3ad47..79a137d 100644
--- a/plugins/quectel.c
+++ b/plugins/quectel.c
@@ -64,7 +64,7 @@ static const char *cpin_prefix[] = { "+CPIN:", NULL };
 static const char *cbc_prefix[] = { "+CBC:", NULL };
 static const char *qinistat_prefix[] = { "+QINISTAT:", NULL };
 static const char *cgmm_prefix[] = { "UC15", "Quectel_M95", "Quectel_MC60",
-					NULL };
+					"EC21", "EC200", NULL };
 static const char *none_prefix[] = { NULL };
 
 static const uint8_t gsm0710_terminate[] = {
@@ -83,6 +83,8 @@ enum quectel_model {
 	QUECTEL_UC15,
 	QUECTEL_M95,
 	QUECTEL_MC60,
+	QUECTEL_EC21,
+	QUECTEL_EC200,
 };
 
 struct quectel_data {
@@ -103,6 +105,7 @@ struct quectel_data {
 	int initial_ldisc;
 	struct l_gpio_writer *gpio;
 	struct l_timeout *init_timeout;
+	struct l_timeout *gpio_timeout;
 	size_t init_count;
 	guint init_cmd;
 };
@@ -125,6 +128,15 @@ enum quectel_power_event {
 
 static const char dbus_hw_interface[] = OFONO_SERVICE ".quectel.Hardware";
 
+static ofono_bool_t has_serial_connection(struct ofono_modem *modem)
+{
+
+	if (ofono_modem_get_string(modem, "Device"))
+		return TRUE;
+
+	return FALSE;
+}
+
 static void quectel_debug(const char *str, void *user_data)
 {
 	const char *prefix = user_data;
@@ -190,6 +202,7 @@ static void quectel_remove(struct ofono_modem *modem)
 
 	ofono_modem_set_data(modem, NULL);
 	l_timeout_remove(data->init_timeout);
+	l_timeout_remove(data->gpio_timeout);
 	l_gpio_writer_free(data->gpio);
 	at_util_sim_state_query_free(data->sim_state_query);
 	g_at_chat_unref(data->aux);
@@ -233,10 +246,22 @@ static void close_ngsm(struct ofono_modem *modem)
 		ofono_warn("Failed to restore line discipline");
 }
 
+static void gpio_power_off_cb(struct l_timeout *timeout, void *user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct quectel_data *data = ofono_modem_get_data(modem);
+	const uint32_t gpio_value = 0;
+
+	l_timeout_remove(timeout);
+	data->gpio_timeout = NULL;
+	l_gpio_writer_set(data->gpio, 1, &gpio_value);
+	ofono_modem_set_powered(modem, FALSE);
+}
+
 static void close_serial(struct ofono_modem *modem)
 {
 	struct quectel_data *data = ofono_modem_get_data(modem);
-	uint32_t gpio_value = 0;
+	uint32_t gpio_value = 1;
 
 	DBG("%p", modem);
 
@@ -257,7 +282,20 @@ static void close_serial(struct ofono_modem *modem)
 	else
 		close_ngsm(modem);
 
-	l_gpio_writer_set(data->gpio, 1, &gpio_value);
+	if (data->gpio) {
+		if (ofono_modem_get_boolean(modem, "GpioLevel")) {
+			gpio_value = 0;
+			l_gpio_writer_set(data->gpio, 1, &gpio_value);
+		} else {
+			l_gpio_writer_set(data->gpio, 1, &gpio_value);
+			l_timeout_remove(data->gpio_timeout);
+			data->gpio_timeout = l_timeout_create_ms(750,
+							gpio_power_off_cb,
+							modem, NULL);
+			return;
+		}
+	}
+
 	ofono_modem_set_powered(modem, FALSE);
 }
 
@@ -424,10 +462,12 @@ static void qind_notify(GAtResult *result, void *user_data)
 	if (!g_at_result_iter_next_string(&iter, &type))
 		return;
 
-	if (!g_at_result_iter_next_number(&iter, &event))
-		return;
+	if (g_strcmp0("vbatt", type)) {
+		if (!g_at_result_iter_next_number(&iter, &event))
+			return;
 
-	voltage_handle(hw->modem, event);
+		voltage_handle(hw->modem, event);
+	}
 }
 
 static void power_notify(GAtResult *result, void *user_data)
@@ -512,6 +552,8 @@ static void dbus_hw_enable(struct ofono_modem *modem)
 
 	switch (data->model) {
 	case QUECTEL_UC15:
+	case QUECTEL_EC21:
+	case QUECTEL_EC200:
 		g_at_chat_register(data->aux, "+QIND",  qind_notify, FALSE, hw,
 					NULL);
 		break;
@@ -556,9 +598,17 @@ static void qinistat_cb(gboolean ok, GAtResult *result, gpointer user_data)
 
 	switch (data->model) {
 	case QUECTEL_UC15:
+	case QUECTEL_EC21:
 		/* UC15 uses a bitmap of 1 + 2 + 4 = 7 */
 		ready = 7;
 		break;
+	case QUECTEL_EC200:
+		/*
+		 * EC200T doesn't indicate that the Phonebook initialization
+		 * is completed (==4) when AT+CFUN=4, that's why 1 + 2 = 3
+		 */
+		ready = 3;
+		break;
 	case QUECTEL_M95:
 	case QUECTEL_MC60:
 		/* M95 and MC60 uses a counter to 3 */
@@ -762,6 +812,30 @@ static void cfun_query(gboolean ok, GAtResult *result, gpointer user_data)
 		cfun_enable(TRUE, NULL, modem);
 }
 
+static void setup_aux(struct ofono_modem *modem)
+{
+	struct quectel_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	g_at_chat_set_slave(data->modem, data->aux);
+
+	if (data->model == QUECTEL_EC21) {
+		g_at_chat_send(data->aux, "ATE0; &C0; +CMEE=1", none_prefix,
+				NULL, NULL, NULL);
+		g_at_chat_send(data->aux, "AT+QURCCFG=\"urcport\",\"uart1\"", none_prefix,
+				NULL, NULL, NULL);
+	} else if (data->model == QUECTEL_EC200) {
+		g_at_chat_send(data->aux, "ATE0; &C0; +CMEE=1", none_prefix,
+				NULL, NULL, NULL);
+	} else
+		g_at_chat_send(data->aux, "ATE0; &C0; +CMEE=1; +QIURC=0",
+				none_prefix, NULL, NULL, NULL);
+
+	g_at_chat_send(data->aux, "AT+CFUN?", cfun_prefix, cfun_query, modem,
+			NULL);
+}
+
 static void cgmm_cb(int ok, GAtResult *result, void *user_data)
 {
 	struct ofono_modem *modem = user_data;
@@ -788,25 +862,29 @@ static void cgmm_cb(int ok, GAtResult *result, void *user_data)
 		DBG("%p model MC60", modem);
 		data->vendor = OFONO_VENDOR_QUECTEL_SERIAL;
 		data->model = QUECTEL_MC60;
+	} else if (strcmp(model, "EC21") == 0) {
+		DBG("%p model EC21", modem);
+		data->vendor = OFONO_VENDOR_QUECTEL_EC2X;
+		data->model = QUECTEL_EC21;
+	} else if (strstr(model, "EC200")) {
+		DBG("%p model %s", modem, model);
+		data->vendor = OFONO_VENDOR_QUECTEL_EC2X;
+		data->model = QUECTEL_EC200;
 	} else {
 		ofono_warn("%p unknown model: '%s'", modem, model);
 		data->vendor = OFONO_VENDOR_QUECTEL;
 		data->model = QUECTEL_UNKNOWN;
 	}
 
-	g_at_chat_send(data->aux, "AT+CFUN?", cfun_prefix, cfun_query, modem,
-			NULL);
+	setup_aux(modem);
 }
 
-static void setup_aux(struct ofono_modem *modem)
+static void identify_model(struct ofono_modem *modem)
 {
 	struct quectel_data *data = ofono_modem_get_data(modem);
 
 	DBG("%p", modem);
 
-	g_at_chat_set_slave(data->modem, data->aux);
-	g_at_chat_send(data->aux, "ATE0; &C0; +CMEE=1; +QIURC=0", none_prefix,
-			NULL, NULL, NULL);
 	g_at_chat_send(data->aux, "AT+CGMM", cgmm_prefix, cgmm_cb, modem,
 			NULL);
 }
@@ -830,7 +908,7 @@ static int open_ttys(struct ofono_modem *modem)
 		return -EIO;
 	}
 
-	setup_aux(modem);
+	identify_model(modem);
 
 	return -EINPROGRESS;
 }
@@ -894,7 +972,7 @@ static void cmux_gatmux(struct ofono_modem *modem)
 		return;
 	}
 
-	setup_aux(modem);
+	identify_model(modem);
 }
 
 static void mux_ready_cb(struct l_timeout *timeout, void *user_data)
@@ -1035,7 +1113,7 @@ static void ate_cb(int ok, GAtResult *result, void *user_data)
 	DBG("%p", modem);
 
 	g_at_chat_send(data->uart, "AT+CMUX=0,0,5,127,10,3,30,10,2", NULL,
-			cmux_cb, modem, NULL);
+		cmux_cb, modem, NULL);
 }
 
 static void init_cmd_cb(gboolean ok, GAtResult *result, void *user_data)
@@ -1069,8 +1147,8 @@ static void init_timeout_cb(struct l_timeout *timeout, void *user_data)
 
 	DBG("%p", modem);
 
-	if (data->init_count++ >= 20) {
-		ofono_error("failed to init modem after 20 attempts");
+	if (data->init_count++ >= 30) {
+		ofono_error("failed to init modem after 30 attempts");
 		close_serial(modem);
 		return;
 	}
@@ -1079,6 +1157,16 @@ static void init_timeout_cb(struct l_timeout *timeout, void *user_data)
 	l_timeout_modify_ms(timeout, 500);
 }
 
+static void gpio_power_on_cb(struct l_timeout *timeout, void *user_data)
+{
+	struct quectel_data *data = user_data;
+	const uint32_t gpio_value = 0;
+
+	l_timeout_remove(timeout);
+	data->gpio_timeout = NULL;
+	l_gpio_writer_set(data->gpio, 1, &gpio_value);
+}
+
 static int open_serial(struct ofono_modem *modem)
 {
 	struct quectel_data *data = ofono_modem_get_data(modem);
@@ -1122,6 +1210,10 @@ static int open_serial(struct ofono_modem *modem)
 		return -EIO;
 	}
 
+	if (data->gpio && !ofono_modem_get_boolean(modem, "GpioLevel"))
+		data->gpio_timeout = l_timeout_create_ms(2100, gpio_power_on_cb,
+							 data, NULL);
+
 	/*
 	 * there are three different power-up scenarios:
 	 *
@@ -1153,7 +1245,7 @@ static int quectel_enable(struct ofono_modem *modem)
 {
 	DBG("%p", modem);
 
-	if (ofono_modem_get_string(modem, "Device"))
+	if (has_serial_connection(modem))
 		return open_serial(modem);
 	else
 		return open_ttys(modem);
@@ -1228,6 +1320,8 @@ static void quectel_pre_sim(struct ofono_modem *modem)
 
 	DBG("%p", modem);
 
+	ofono_devinfo_create(modem, data->vendor, "atmodem", data->aux);
+
 	ofono_voicecall_create(modem, data->vendor, "atmodem", data->aux);
 	sim = ofono_sim_create(modem, data->vendor, "atmodem", data->aux);
 
@@ -1256,6 +1350,11 @@ static void quectel_post_sim(struct ofono_modem *modem)
 	ofono_sms_create(modem, data->vendor, "atmodem", data->aux);
 	ofono_phonebook_create(modem, data->vendor, "atmodem", data->aux);
 	ofono_call_volume_create(modem, data->vendor, "atmodem", data->aux);
+
+	if (data->model == QUECTEL_EC21 || data->model == QUECTEL_EC200) {
+		ofono_ussd_create(modem, data->vendor, "atmodem", data->aux);
+		ofono_lte_create(modem, data->vendor, "atmodem", data->aux);
+	}
 }
 
 static void quectel_post_online(struct ofono_modem *modem)
@@ -1264,7 +1363,7 @@ static void quectel_post_online(struct ofono_modem *modem)
 
 	DBG("%p", modem);
 
-	ofono_netreg_create(modem, 0, "atmodem", data->aux);
+	ofono_netreg_create(modem, data->vendor, "atmodem", data->aux);
 }
 
 static struct ofono_modem_driver quectel_driver = {
diff --git a/plugins/udevng.c b/plugins/udevng.c
index 4a38621..34ac1cc 100644
--- a/plugins/udevng.c
+++ b/plugins/udevng.c
@@ -712,6 +712,34 @@ static gboolean setup_telitqmi(struct modem_info *modem)
 	return TRUE;
 }
 
+static gboolean setup_droid(struct modem_info *modem)
+{
+	const char *at = NULL;
+	GSList *list;
+
+	DBG("%s", modem->syspath);
+
+	for (list = modem->devices; list; list = list->next) {
+		struct device_info *info = list->data;
+
+		DBG("%s %s %s %s %s", info->devnode, info->interface,
+				info->number, info->label, info->subsystem);
+
+		if (g_strcmp0(info->interface, "255/255/255") == 0 &&
+				g_strcmp0(info->number, "04") == 0) {
+			at = info->devnode;
+		}
+	}
+
+	if (at == NULL)
+		return FALSE;
+
+	ofono_modem_set_string(modem->modem, "Device", at);
+	ofono_modem_set_driver(modem->modem, "droid");
+
+	return TRUE;
+}
+
 /* TODO: Not used as we have no simcom driver */
 static gboolean setup_simcom(struct modem_info *modem)
 {
@@ -893,6 +921,11 @@ static gboolean setup_quectel_serial(struct modem_info *modem)
 	if (value)
 		ofono_modem_set_string(modem->modem, "GpioOffset", value);
 
+	value = udev_device_get_property_value(info->dev,
+						"OFONO_QUECTEL_GPIO_LEVEL");
+	if (value)
+		ofono_modem_set_boolean(modem->modem, "GpioLevel", TRUE);
+
 	value = udev_device_get_property_value(info->dev,
 						"OFONO_QUECTEL_MUX");
 	if (value)
@@ -908,10 +941,12 @@ static gboolean setup_quectel_serial(struct modem_info *modem)
 
 static gboolean setup_quectel(struct modem_info *modem)
 {
-	if (modem->serial)
+	if (modem->type == MODEM_TYPE_SERIAL)
 		return setup_quectel_serial(modem);
-	else
+	else if (modem->type == MODEM_TYPE_USB)
 		return setup_quectel_usb(modem);
+	else
+		return FALSE;
 }
 
 static gboolean setup_quectelqmi(struct modem_info *modem)
@@ -1162,7 +1197,7 @@ static gboolean setup_ublox(struct modem_info *modem)
 static gboolean setup_gemalto(struct modem_info* modem)
 {
 	const char *app = NULL, *gps = NULL, *mdm = NULL,
-		*net = NULL, *qmi = NULL;
+		*net = NULL, *qmi = NULL, *net2 = NULL;
 
 	GSList *list;
 
@@ -1197,9 +1232,14 @@ static gboolean setup_gemalto(struct modem_info* modem)
 			else if (g_strcmp0(info->number, "04") == 0)
 				gps = info->devnode;
 		}
+
 		if (g_strcmp0(info->interface, "2/6/0") == 0) {
-			if (g_strcmp0(info->subsystem, "net") == 0)
-				net = info->devnode;
+			if (g_strcmp0(info->subsystem, "net") == 0) {
+				if (g_strcmp0(info->number, "0a") == 0)
+					net = info->devnode;
+				if (g_strcmp0(info->number, "0c") == 0)
+					net2 = info->devnode;
+			}
 		}
 	}
 
@@ -1216,6 +1256,9 @@ static gboolean setup_gemalto(struct modem_info* modem)
 	ofono_modem_set_string(modem->modem, "Model", modem->model);
 	ofono_modem_set_string(modem->modem, "NetworkInterface", net);
 
+	if (net2)
+		ofono_modem_set_string(modem->modem, "NetworkInterface2", net2);
+
 	return TRUE;
 }
 
@@ -1401,6 +1444,7 @@ static struct {
 	{ "gemalto",	setup_gemalto	},
 	{ "xmm7xxx",	setup_xmm7xxx	},
 	{ "mbim",	setup_mbim	},
+	{ "droid",	setup_droid	},
 	/* Following are non-USB modems */
 	{ "ifx",	setup_ifx		},
 	{ "u8500",	setup_isi_serial	},
@@ -1786,6 +1830,8 @@ static struct {
 	{ "telit",	"cdc_acm",	"1bc7", "0021"	},
 	{ "telitqmi",	"qmi_wwan",	"1bc7", "1201"	},
 	{ "telitqmi",	"option",	"1bc7", "1201"	},
+	{ "droid",	"qmi_wwan",	"22b8", "2a70"	},
+	{ "droid",	"option",	"22b8", "2a70"	},
 	{ "nokia",	"option",	"0421", "060e"	},
 	{ "nokia",	"option",	"0421", "0623"	},
 	{ "samsung",	"option",	"04e8", "6889"	},
@@ -1795,6 +1841,8 @@ static struct {
 	{ "quectelqmi",	"qcserial",	"2c7c", "0121"	},
 	{ "quectelqmi",	"qmi_wwan",	"2c7c", "0125"	},
 	{ "quectelqmi",	"qcserial",	"2c7c", "0125"	},
+	{ "quectelqmi",	"qmi_wwan",	"2c7c", "0296"	},
+	{ "quectelqmi",	"qcserial",	"2c7c", "0296"	},
 	{ "ublox",	"cdc_acm",	"1546", "1010"	},
 	{ "ublox",	"cdc_ncm",	"1546", "1010"	},
 	{ "ublox",	"cdc_acm",	"1546", "1102"	},
@@ -1807,6 +1855,8 @@ static struct {
 	{ "gemalto",	"qmi_wwan",	"1e2d",	"0053"	},
 	{ "gemalto",	"cdc_acm",	"1e2d",	"0061"	},
 	{ "gemalto",	"cdc_ether",	"1e2d",	"0061"	},
+	{ "gemalto",	"cdc_acm",	"1e2d",	"005b"	},
+	{ "gemalto",	"cdc_ether",	"1e2d",	"005b"	},
 	{ "telit",	"cdc_ncm",	"1bc7", "0036"	},
 	{ "telit",	"cdc_acm",	"1bc7", "0036"	},
 	{ "xmm7xxx",	"cdc_acm",	"8087"		},
diff --git a/plugins/xmm7xxx.c b/plugins/xmm7xxx.c
index a544798..5efdbe2 100644
--- a/plugins/xmm7xxx.c
+++ b/plugins/xmm7xxx.c
@@ -59,9 +59,11 @@
 
 #include "ofono.h"
 #include "gdbus.h"
+#include "util.h"
 
 #define OFONO_COEX_INTERFACE OFONO_SERVICE ".intel.LteCoexistence"
 #define OFONO_COEX_AGENT_INTERFACE OFONO_SERVICE ".intel.LteCoexistenceAgent"
+#define OFONO_EUICC_LPA_INTERFACE OFONO_SERVICE ".intel.EuiccLpa"
 
 #define NET_BAND_LTE_INVALID 0
 #define NET_BAND_LTE_1 101
@@ -73,6 +75,8 @@
 static const char *none_prefix[] = { NULL };
 static const char *xsimstate_prefix[] = { "+XSIMSTATE:", NULL };
 static const char *xnvmplmn_prefix[] = { "+XNVMPLMN:", NULL };
+static const char *ccho_prefix[] = { "+CCHO:", NULL };
+static const char *cgla_prefix[] = { "+CGLA:", NULL };
 
 struct bt_coex_info {
 	int safe_tx_min;
@@ -106,10 +110,382 @@ struct xmm7xxx_data {
 	GAtChat *chat;		/* AT chat */
 	struct ofono_sim *sim;
 	ofono_bool_t have_sim;
-	ofono_bool_t sms_phonebook_added;
 	unsigned int netreg_watch;
+	int xsim_status;
+	ofono_bool_t stk_enable;
+	ofono_bool_t enable_euicc;
 };
 
+/* eUICC Implementation */
+#define EUICC_EID_CMD "80e2910006BF3E035C015A00"
+#define EUICC_ISDR_AID "A0000005591010FFFFFFFF8900000100"
+
+struct xmm7xxx_euicc {
+	GAtChat *chat;
+	struct ofono_modem *modem;
+	char *eid;
+	int channel;
+	char *command;
+	int length;
+	DBusMessage *pending;
+	ofono_bool_t is_registered;
+};
+
+static void euicc_cleanup(void *data)
+{
+	struct xmm7xxx_euicc *euicc = data;
+
+	g_free(euicc->command);
+	g_free(euicc->eid);
+
+	if (euicc->pending)
+		dbus_message_unref(euicc->pending);
+
+	g_free(euicc);
+}
+
+static void euicc_release_isdr(struct xmm7xxx_euicc *euicc)
+{
+	char buff[20];
+
+	snprintf(buff, sizeof(buff), "AT+CCHC=%u", euicc->channel);
+
+	g_at_chat_send(euicc->chat, buff, none_prefix, NULL, NULL, NULL);
+
+	euicc->channel = -1;
+	g_free(euicc->command);
+	euicc->command = NULL;
+	euicc->length = 0;
+}
+
+static void euicc_pending_reply(struct xmm7xxx_euicc *euicc,
+							const char *resp)
+{
+	DBusMessage *reply;
+	DBusMessageIter iter, array;
+	unsigned char *response = NULL;
+	long length;
+	int bufferBytesSize = strlen(resp) / 2;
+
+	reply = dbus_message_new_method_return(euicc->pending);
+	if (reply == NULL)
+		goto done;
+
+	response = g_new0(unsigned char, bufferBytesSize);
+	decode_hex_own_buf(resp, strlen(resp),  &length, '\0', response );
+
+	dbus_message_iter_init_append(reply, &iter);
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+				DBUS_TYPE_BYTE_AS_STRING, &array);
+	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+					&response, length);
+	dbus_message_iter_close_container(&iter, &array);
+
+	g_free(response);
+done:
+	__ofono_dbus_pending_reply(&euicc->pending, reply);
+}
+
+static DBusMessage *euicc_get_properties(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct xmm7xxx_euicc *euicc = data;
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	const char *eid = NULL;
+
+	reply = dbus_message_new_method_return(msg);
+	if (reply == NULL)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					OFONO_PROPERTIES_ARRAY_SIGNATURE,
+					&dict);
+
+	eid = euicc->eid;
+	ofono_dbus_dict_append(&dict, "EID", DBUS_TYPE_STRING, &eid);
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	return reply;
+}
+
+static DBusMessage *euicc_transmit_pdu(DBusConnection *conn,
+					DBusMessage *msg, void *data);
+static DBusMessage *euicc_select_isdr_req(DBusConnection *conn,
+						DBusMessage *msg, void *data);
+static DBusMessage *euicc_release_isdr_req(DBusConnection *conn,
+						DBusMessage *msg, void *data);
+
+static const GDBusMethodTable euicc_methods[] = {
+	{ GDBUS_ASYNC_METHOD("TransmitLpaApdu",
+			GDBUS_ARGS({ "pdu", "ay" }),
+			GDBUS_ARGS({ "pdu", "ay" }),
+			euicc_transmit_pdu) },
+	{ GDBUS_ASYNC_METHOD("SelectISDR",
+			NULL, NULL, euicc_select_isdr_req) },
+	{ GDBUS_ASYNC_METHOD("ReleaseISDR",
+			NULL, NULL, euicc_release_isdr_req) },
+	{ GDBUS_ASYNC_METHOD("GetProperties",
+			NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+			euicc_get_properties) },
+	{ }
+};
+
+static const GDBusSignalTable euicc_signals[] = {
+	{ GDBUS_SIGNAL("PropertyChanged",
+			GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+	{ }
+};
+
+static void euicc_register(struct xmm7xxx_euicc *euicc)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = ofono_modem_get_path(euicc->modem);
+
+	DBG("euicc_register");
+
+	if (!g_dbus_register_interface(conn, path, OFONO_EUICC_LPA_INTERFACE,
+					euicc_methods,
+					euicc_signals,
+					NULL, euicc, euicc_cleanup)) {
+		ofono_error("Could not register %s interface under %s",
+					OFONO_EUICC_LPA_INTERFACE, path);
+		return;
+	}
+
+	ofono_modem_add_interface(euicc->modem, OFONO_EUICC_LPA_INTERFACE);
+	euicc->is_registered = TRUE;
+
+	ofono_dbus_signal_property_changed(conn, path,
+			OFONO_EUICC_LPA_INTERFACE, "EID",
+			DBUS_TYPE_STRING, &euicc->eid);
+}
+
+static void euicc_send_cmd_cb(gboolean ok, GAtResult *result,
+					gpointer user_data)
+{
+	struct xmm7xxx_euicc *euicc = user_data;
+	GAtResultIter iter;
+	int length;
+	const char *resp;
+
+	DBG("ok %d", ok);
+
+	if (!ok) {
+		g_free(euicc->command);
+
+		if (!euicc->is_registered) {
+			g_free(euicc->eid);
+			g_free(euicc);
+		}
+
+		return;
+	}
+
+	DBG("Success");
+
+	g_at_result_iter_init(&iter, result);
+	DBG("Iter init");
+
+	if (!g_at_result_iter_next(&iter, "+CGLA:"))
+		return;
+
+	DBG("CGLA");
+
+	if (!g_at_result_iter_next_number(&iter, &length))
+		return;
+
+	DBG("length = %d", length);
+
+	if (!g_at_result_iter_next_string(&iter, &resp))
+		return;
+
+	DBG("resp = %s", resp);
+
+	if (!euicc->is_registered) {
+		g_free(euicc->eid);
+		euicc->eid = g_strdup(resp+10);
+		euicc_release_isdr(euicc);
+
+		/* eid is present register interface*/
+		euicc_register(euicc);
+	}
+
+	DBG("pending = %p", euicc->pending);
+
+	if (euicc->pending)
+		euicc_pending_reply(euicc, resp);
+}
+
+static void euicc_send_cmd(struct xmm7xxx_euicc *euicc)
+{
+	char *buff = g_new0(char, euicc->length + 20);
+
+	sprintf(buff, "AT+CGLA=%u,%u,\"%s\"",
+		euicc->channel, euicc->length, euicc->command);
+
+	g_at_chat_send(euicc->chat, buff, cgla_prefix,
+			euicc_send_cmd_cb, euicc, NULL);
+
+	g_free(buff);
+}
+
+static void euicc_select_isdr_cb(gboolean ok, GAtResult *result,
+							gpointer user_data)
+{
+	struct xmm7xxx_euicc *euicc = user_data;
+	GAtResultIter iter;
+
+	DBG("ok %d", ok);
+
+	if (!ok) {
+		g_free (euicc->command);
+
+		if (!euicc->is_registered) {
+			g_free(euicc->eid);
+			g_free(euicc);
+		}
+
+		return;
+	}
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+CCHO:"))
+		return;
+
+	g_at_result_iter_next_number(&iter, &euicc->channel);
+
+	if (!euicc->is_registered)
+		euicc_send_cmd(euicc);
+
+	if (euicc->pending)
+		__ofono_dbus_pending_reply(&euicc->pending,
+				dbus_message_new_method_return(euicc->pending));
+}
+
+static void euicc_select_isdr(struct xmm7xxx_euicc *euicc)
+{
+	char buff[50];
+
+	snprintf(buff, sizeof(buff), "AT+CCHO=\"%s\"", EUICC_ISDR_AID);
+
+	g_at_chat_send(euicc->chat, buff, ccho_prefix,
+			euicc_select_isdr_cb, euicc, NULL);
+}
+
+static DBusMessage *euicc_transmit_pdu(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct xmm7xxx_euicc *euicc = data;
+	DBusMessageIter iter, array;
+	const unsigned char *command;
+	int length;
+
+	DBG("euicc_transmit_pdu");
+
+	if (euicc->pending)
+		return __ofono_error_busy(msg);
+
+	if (euicc->channel < 0)
+		return __ofono_error_not_available(msg);
+
+	if (!dbus_message_iter_init(msg, &iter))
+		return __ofono_error_invalid_args(msg);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+		return __ofono_error_invalid_args(msg);
+
+	dbus_message_iter_recurse(&iter, &array);
+
+	if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE)
+		return __ofono_error_invalid_args(msg);
+
+	dbus_message_iter_get_fixed_array(&array, &command, &length);
+
+	g_free(euicc->command);
+	euicc->length = length * 2;
+	euicc->command = g_new0(char, euicc->length + 1);
+	encode_hex_own_buf(command,(long)length,0, euicc->command);
+	euicc->pending = dbus_message_ref(msg);
+
+	euicc_send_cmd(euicc);
+
+	return NULL;
+}
+
+static DBusMessage *euicc_select_isdr_req(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct xmm7xxx_euicc *euicc = data;
+
+	DBG("euicc_select_isdr_req");
+
+	if (euicc->pending)
+		return __ofono_error_busy(msg);
+
+	euicc_select_isdr(euicc);
+
+	euicc->pending = dbus_message_ref(msg);
+
+	return NULL;
+}
+
+static DBusMessage *euicc_release_isdr_req(DBusConnection *conn,
+						DBusMessage *msg, void *data)
+{
+	struct xmm7xxx_euicc *euicc = data;
+
+	DBG("euicc_release_isdr_req");
+
+	if (euicc->pending)
+		return __ofono_error_busy(msg);
+
+	euicc_release_isdr(euicc);
+
+	return dbus_message_new_method_return(msg);
+}
+
+static void euicc_update_eid(struct xmm7xxx_euicc *euicc)
+{
+	g_free(euicc->command);
+	euicc->command = g_strdup(EUICC_EID_CMD);
+	euicc->length = sizeof(EUICC_EID_CMD) - 1;
+
+	euicc_select_isdr(euicc);
+}
+
+static void xmm_euicc_enable(struct ofono_modem *modem, void *data)
+{
+	struct xmm7xxx_euicc *euicc = g_new0(struct xmm7xxx_euicc, 1);
+
+	DBG("euicc enable");
+
+	euicc->chat = data;
+	euicc->modem = modem;
+	euicc->eid = g_strdup("INVALID");
+	euicc->channel = -1;
+	euicc->command = NULL;
+	euicc->pending = NULL;
+	euicc->is_registered = FALSE;
+
+	euicc_update_eid(euicc);
+}
+
+static void xmm_euicc_disable(struct ofono_modem *modem)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = ofono_modem_get_path(modem);
+
+	if (g_dbus_unregister_interface(conn, path, OFONO_EUICC_LPA_INTERFACE))
+		ofono_modem_remove_interface(modem, OFONO_EUICC_LPA_INTERFACE);
+}
+/* eUICC Implementation Ends */
+
 /* Coex Implementation */
 enum wlan_bw {
 	WLAN_BW_UNSUPPORTED = -1,
@@ -570,8 +946,6 @@ static DBusMessage *coex_set_property(DBusConnection *conn,
 	} else {
 		return __ofono_error_invalid_args(msg);
 	}
-
-	return dbus_message_new_method_return(msg);
 }
 
 static void coex_default_agent_notify(gpointer user_data)
@@ -982,10 +1356,10 @@ static void switch_sim_state_status(struct ofono_modem *modem, int status)
 		if (data->have_sim == TRUE) {
 			ofono_sim_inserted_notify(data->sim, FALSE);
 			data->have_sim = FALSE;
-			data->sms_phonebook_added = FALSE;
 		}
 		break;
 	case 1: /* SIM inserted, PIN verification needed */
+	case 4: /* SIM inserted, PUK verification needed */
 		if (data->have_sim == FALSE) {
 			ofono_sim_inserted_notify(data->sim, TRUE);
 			data->have_sim = TRUE;
@@ -993,30 +1367,29 @@ static void switch_sim_state_status(struct ofono_modem *modem, int status)
 		break;
 	case 2:	/* SIM inserted, PIN verification not needed - READY */
 	case 3:	/* SIM inserted, PIN verified - READY */
-	case 7: /* SIM inserted, SMS and phonebook - READY */
+	case 7: /* SIM inserted, Ready for ATTACH - READY */
 		if (data->have_sim == FALSE) {
 			ofono_sim_inserted_notify(data->sim, TRUE);
 			data->have_sim = TRUE;
 		}
 
 		ofono_sim_initialized_notify(data->sim);
-
-		if (data->sms_phonebook_added == FALSE) {
-			ofono_phonebook_create(modem, 0, "atmodem", data->chat);
-			ofono_sms_create(modem, 0, "atmodem", data->chat);
-			data->sms_phonebook_added = TRUE;
-		}
-
+		break;
+	case 18:
+		data->enable_euicc = TRUE;
 		break;
 	default:
 		ofono_warn("Unknown SIM state %d received", status);
 		break;
 	}
+
+	data->xsim_status = status;
 }
 
 static void xsimstate_notify(GAtResult *result, gpointer user_data)
 {
 	struct ofono_modem *modem = user_data;
+	struct xmm7xxx_data *data = ofono_modem_get_data(modem);
 	int status;
 	GAtResultIter iter;
 
@@ -1031,7 +1404,8 @@ static void xsimstate_notify(GAtResult *result, gpointer user_data)
 
 	DBG("status=%d\n", status);
 
-	switch_sim_state_status(modem, status);
+	if (data->xsim_status != status)
+		switch_sim_state_status(modem, status);
 }
 
 static void xsimstate_query_cb(gboolean ok, GAtResult *result,
@@ -1085,7 +1459,7 @@ static void cfun_enable_cb(gboolean ok, GAtResult *result, gpointer user_data)
 	g_at_chat_send(data->chat, "AT&C0", NULL, NULL, NULL, NULL);
 
 	data->have_sim = FALSE;
-	data->sms_phonebook_added = FALSE;
+	data->xsim_status = -1;
 
 	ofono_modem_set_powered(modem, TRUE);
 
@@ -1181,6 +1555,7 @@ static int xmm7xxx_disable(struct ofono_modem *modem)
 		data->netreg_watch = 0;
 	}
 
+	xmm_euicc_disable(modem);
 	return -EINPROGRESS;
 }
 
@@ -1193,6 +1568,8 @@ static void xmm7xxx_pre_sim(struct ofono_modem *modem)
 	ofono_devinfo_create(modem, OFONO_VENDOR_IFX, "atmodem", data->chat);
 	data->sim = ofono_sim_create(modem, OFONO_VENDOR_XMM, "atmodem",
 					data->chat);
+	xmm_euicc_enable(modem, data->chat);
+	ofono_stk_create(modem, 0, "atmodem", data->chat);
 }
 
 static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
@@ -1200,8 +1577,15 @@ static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
 	struct cb_data *cbd = user_data;
 	ofono_modem_online_cb_t cb = cbd->cb;
 	struct ofono_error error;
+	struct ofono_modem *modem = cbd->data;
+	struct xmm7xxx_data *data = ofono_modem_get_data(modem);
 
 	decode_at_error(&error, g_at_result_final_response(result));
+
+	if (data->enable_euicc == TRUE && data->stk_enable == TRUE)
+		g_at_chat_send(data->chat, "AT+CFUN=16", none_prefix,
+							NULL, NULL, NULL);
+
 	cb(&error, cbd->data);
 }
 
@@ -1213,6 +1597,7 @@ static void xmm7xxx_set_online(struct ofono_modem *modem, ofono_bool_t online,
 	char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4";
 
 	DBG("modem %p %s", modem, online ? "online" : "offline");
+	data->stk_enable = online;
 
 	if (g_at_chat_send(data->chat, command, none_prefix,
 					set_online_cb, cbd, g_free) > 0)
@@ -1241,6 +1626,9 @@ static void xmm7xxx_post_online(struct ofono_modem *modem)
 
 	DBG("%p", modem);
 
+	ofono_phonebook_create(modem, 0, "atmodem", data->chat);
+	ofono_sms_create(modem, 0, "atmodem", data->chat);
+
 	ofono_netreg_create(modem, OFONO_VENDOR_IFX, "atmodem", data->chat);
 
 	gprs = ofono_gprs_create(modem, OFONO_VENDOR_IFX, "atmodem",
diff --git a/src/common.c b/src/common.c
index 4dcbc83..f955a76 100644
--- a/src/common.c
+++ b/src/common.c
@@ -709,7 +709,7 @@ const char *registration_tech_to_string(int tech)
 gboolean is_valid_apn(const char *apn)
 {
 	int i;
-	int last_period = 0;
+	int last_period = -1;
 
 	if (apn == NULL)
 		return FALSE;
diff --git a/src/gprs.c b/src/gprs.c
index 170f615..950b40f 100644
--- a/src/gprs.c
+++ b/src/gprs.c
@@ -223,23 +223,11 @@ static gboolean gprs_context_string_to_type(const char *str,
 	return FALSE;
 }
 
-static gboolean assign_context(struct pri_context *ctx, unsigned int use_cid)
+static struct ofono_gprs_context *find_avail_gprs_context(
+							struct pri_context *ctx)
 {
-	struct l_uintset *used_cids = ctx->gprs->used_cids;
 	GSList *l;
 
-	if (used_cids == NULL)
-		return FALSE;
-
-	if (!use_cid)
-		use_cid = l_uintset_find_unused_min(used_cids);
-
-	if (use_cid > l_uintset_get_max(used_cids))
-		return FALSE;
-
-	l_uintset_put(used_cids, use_cid);
-	ctx->context.cid = use_cid;
-
 	for (l = ctx->gprs->context_drivers; l; l = l->next) {
 		struct ofono_gprs_context *gc = l->data;
 
@@ -257,24 +245,45 @@ static gboolean assign_context(struct pri_context *ctx, unsigned int use_cid)
 				gc->type != ctx->type)
 			continue;
 
-		ctx->context_driver = gc;
-		ctx->context_driver->inuse = TRUE;
+		return gc;
+	}
 
-		if (ctx->context.proto == OFONO_GPRS_PROTO_IPV4V6 ||
-				ctx->context.proto == OFONO_GPRS_PROTO_IP)
-			gc->settings->ipv4 = g_new0(struct ipv4_settings, 1);
+	return NULL;
+}
 
-		if (ctx->context.proto == OFONO_GPRS_PROTO_IPV4V6 ||
-				ctx->context.proto == OFONO_GPRS_PROTO_IPV6)
-			gc->settings->ipv6 = g_new0(struct ipv6_settings, 1);
+static gboolean assign_context(struct pri_context *ctx, unsigned int use_cid)
+{
+	struct l_uintset *used_cids = ctx->gprs->used_cids;
+	struct ofono_gprs_context *gc;
 
-		return TRUE;
-	}
+	if (used_cids == NULL)
+		return FALSE;
 
-	l_uintset_take(used_cids, ctx->context.cid);
-	ctx->context.cid = 0;
+	if (!use_cid)
+		use_cid = l_uintset_find_unused_min(used_cids);
 
-	return FALSE;
+	if (use_cid > l_uintset_get_max(used_cids))
+		return FALSE;
+
+	gc = find_avail_gprs_context(ctx);
+	if (gc == NULL)
+		return FALSE;
+
+	l_uintset_put(used_cids, use_cid);
+	ctx->context.cid = use_cid;
+
+	ctx->context_driver = gc;
+	ctx->context_driver->inuse = TRUE;
+
+	if (ctx->context.proto == OFONO_GPRS_PROTO_IPV4V6 ||
+			ctx->context.proto == OFONO_GPRS_PROTO_IP)
+		gc->settings->ipv4 = g_new0(struct ipv4_settings, 1);
+
+	if (ctx->context.proto == OFONO_GPRS_PROTO_IPV4V6 ||
+			ctx->context.proto == OFONO_GPRS_PROTO_IPV6)
+		gc->settings->ipv6 = g_new0(struct ipv6_settings, 1);
+
+	return TRUE;
 }
 
 static void release_context(struct pri_context *ctx)
@@ -1099,7 +1108,7 @@ static DBusMessage *pri_set_name(struct pri_context *ctx, DBusConnection *conn,
 	if (strlen(name) > MAX_CONTEXT_NAME_LENGTH)
 		return __ofono_error_invalid_format(msg);
 
-	if (ctx->name && g_str_equal(ctx->name, name))
+	if (g_str_equal(ctx->name, name))
 		return dbus_message_new_method_return(msg);
 
 	strcpy(ctx->name, name);
@@ -1556,6 +1565,19 @@ static bool have_read_settings(struct ofono_gprs *gprs)
 	return false;
 }
 
+static void pri_context_signal_active(struct pri_context *ctx)
+{
+	DBusConnection *conn;
+	dbus_bool_t value;
+
+	value = ctx->active;
+	conn = ofono_dbus_get_connection();
+
+	ofono_dbus_signal_property_changed(conn, ctx->path,
+					OFONO_CONNECTION_CONTEXT_INTERFACE,
+					"Active", DBUS_TYPE_BOOLEAN, &value);
+}
+
 static void detach_active_contexts(struct ofono_gprs *gprs)
 {
 	GSList *l;
@@ -1579,7 +1601,9 @@ static void detach_active_contexts(struct ofono_gprs *gprs)
 			gc->driver->detach_shutdown(gc, ctx->context.cid);
 
 		/* Make sure the context is properly cleared */
+		pri_reset_context_settings(ctx);
 		release_context(ctx);
+		pri_context_signal_active(ctx);
 	}
 }
 
@@ -2032,7 +2056,6 @@ void ofono_gprs_cid_activated(struct ofono_gprs  *gprs, unsigned int cid,
 
 	if (assign_context(pri_ctx, cid) == FALSE) {
 		ofono_warn("Can't assign context to driver for APN.");
-		release_context(pri_ctx);
 		return;
 	}
 
@@ -2235,8 +2258,6 @@ static void gprs_deactivate_for_all(const struct ofono_error *error,
 {
 	struct pri_context *ctx = data;
 	struct ofono_gprs *gprs = ctx->gprs;
-	DBusConnection *conn;
-	dbus_bool_t value;
 
 	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
 		__ofono_dbus_pending_reply(&gprs->pending,
@@ -2246,12 +2267,7 @@ static void gprs_deactivate_for_all(const struct ofono_error *error,
 
 	pri_reset_context_settings(ctx);
 	release_context(ctx);
-
-	value = ctx->active;
-	conn = ofono_dbus_get_connection();
-	ofono_dbus_signal_property_changed(conn, ctx->path,
-					OFONO_CONNECTION_CONTEXT_INTERFACE,
-					"Active", DBUS_TYPE_BOOLEAN, &value);
+	pri_context_signal_active(ctx);
 
 	gprs_deactivate_next(gprs);
 }
@@ -2730,14 +2746,15 @@ void ofono_gprs_context_deactivated(struct ofono_gprs_context *gc,
 					unsigned int cid)
 {
 	DBusConnection *conn = ofono_dbus_get_connection();
+	struct ofono_gprs *gprs = gc->gprs;
 	GSList *l;
 	struct pri_context *ctx;
 	dbus_bool_t value;
 
-	if (gc->gprs == NULL)
+	if (gprs == NULL)
 		return;
 
-	for (l = gc->gprs->contexts; l; l = l->next) {
+	for (l = gprs->contexts; l; l = l->next) {
 		ctx = l->data;
 
 		if (ctx->context.cid != cid)
@@ -2759,10 +2776,13 @@ void ofono_gprs_context_deactivated(struct ofono_gprs_context *gc,
 	 * If "Attached" property was about to be signalled as TRUE but there
 	 * were still active contexts, try again to signal "Attached" property
 	 * to registered applications after active contexts have been released.
+	 *
+	 * "Attached" could also change to FALSE in case of LTE and getting
+	 * deactivated
 	 */
-	if (gc->gprs->flags & GPRS_FLAG_ATTACHED_UPDATE) {
-		gc->gprs->flags &= ~GPRS_FLAG_ATTACHED_UPDATE;
-		gprs_attached_update(gc->gprs);
+	if (on_lte(gprs) || gprs->flags & GPRS_FLAG_ATTACHED_UPDATE) {
+		gprs->flags &= ~GPRS_FLAG_ATTACHED_UPDATE;
+		gprs_attached_update(gprs);
 	}
 }
 
diff --git a/src/log.c b/src/log.c
index b3bcbc2..b33a7d2 100644
--- a/src/log.c
+++ b/src/log.c
@@ -276,20 +276,11 @@ void __ofono_log_enable(struct ofono_debug_desc *start,
 					struct ofono_debug_desc *stop)
 {
 	struct ofono_debug_desc *desc;
-	const char *name = NULL, *file = NULL;
 
 	if (start == NULL || stop == NULL)
 		return;
 
 	for (desc = start; desc < stop; desc++) {
-		if (file != NULL || name != NULL) {
-			if (g_strcmp0(desc->file, file) == 0) {
-				if (desc->name == NULL)
-					desc->name = name;
-			} else
-				file = NULL;
-		}
-
 		if (is_enabled(desc) == TRUE)
 			desc->flags |= OFONO_DEBUG_FLAG_PRINT;
 	}
diff --git a/src/lte.c b/src/lte.c
index fbe0116..951a06f 100644
--- a/src/lte.c
+++ b/src/lte.c
@@ -212,7 +212,7 @@ static void lte_set_default_attach_info_cb(const struct ofono_error *error,
 	}
 
 	ofono_dbus_signal_property_changed(conn, path,
-					OFONO_CONNECTION_CONTEXT_INTERFACE,
+					OFONO_LTE_INTERFACE,
 					key,
 					DBUS_TYPE_STRING, &value);
 
diff --git a/drivers/gemaltomodem/gemaltomodem.h b/src/missing.h
similarity index 70%
rename from drivers/gemaltomodem/gemaltomodem.h
rename to src/missing.h
index 27b1460..aa96e27 100644
--- a/drivers/gemaltomodem/gemaltomodem.h
+++ b/src/missing.h
@@ -2,8 +2,7 @@
  *
  *  oFono - Open Source Telephony
  *
- *  Copyright (C) 2017 Vincent Cesson. All rights reserved.
- *  Copyright (C) 2018 Gemalto M2M
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2 as
@@ -20,10 +19,6 @@
  *
  */
 
-#include <drivers/atmodem/atutil.h>
-
-extern void gemalto_location_reporting_init();
-extern void gemalto_location_reporting_exit();
-
-extern void gemalto_voicecall_init();
-extern void gemalto_voicecall_exit();
+#ifndef HAVE_G_MEMDUP2
+#define g_memdup2(mem, size) g_memdup((mem), (size))
+#endif
diff --git a/src/modem.c b/src/modem.c
index 52988cf..f4f414c 100644
--- a/src/modem.c
+++ b/src/modem.c
@@ -31,8 +31,8 @@
 #include <gdbus.h>
 
 #include "ofono.h"
-
 #include "common.h"
+#include "missing.h"
 
 #define DEFAULT_POWERED_TIMEOUT (20)
 
@@ -1754,10 +1754,10 @@ static int set_modem_property(struct ofono_modem *modem, const char *name,
 		property->value = g_strdup((const char *) value);
 		break;
 	case PROPERTY_TYPE_INTEGER:
-		property->value = g_memdup(value, sizeof(int));
+		property->value = g_memdup2(value, sizeof(int));
 		break;
 	case PROPERTY_TYPE_BOOLEAN:
-		property->value = g_memdup(value, sizeof(ofono_bool_t));
+		property->value = g_memdup2(value, sizeof(ofono_bool_t));
 		break;
 	default:
 		break;
diff --git a/src/netmon.c b/src/netmon.c
index 9eacb3c..10e3ee2 100644
--- a/src/netmon.c
+++ b/src/netmon.c
@@ -213,6 +213,27 @@ static void netmon_cell_info_dict_append(DBusMessageIter *dict,
 					intval, uint8_t, DBUS_TYPE_BYTE);
 			break;
 
+		case OFONO_NETMON_INFO_PCI:
+			intval = va_arg(*arglist, int);
+
+			CELL_INFO_DICT_APPEND(dict, "PhysicalCellId",
+					intval, uint16_t, DBUS_TYPE_UINT16);
+			break;
+
+		case OFONO_NETMON_INFO_TAC:
+			intval = va_arg(*arglist, int);
+
+			CELL_INFO_DICT_APPEND(dict, "TrackingAreaCode",
+					intval, uint16_t, DBUS_TYPE_UINT16);
+			break;
+
+		case OFONO_NETMON_INFO_SNR:
+			intval = va_arg(*arglist, int);
+
+			ofono_dbus_dict_append(dict, "SingalToNoiseRatio",
+					DBUS_TYPE_INT32, &intval);
+			break;
+
 		case OFONO_NETMON_INFO_INVALID:
 			break;
 		}
diff --git a/src/network.c b/src/network.c
index 2824eae..bfb8817 100644
--- a/src/network.c
+++ b/src/network.c
@@ -36,6 +36,7 @@
 #include "simutil.h"
 #include "util.h"
 #include "storage.h"
+#include "missing.h"
 
 #define SETTINGS_STORE "netreg"
 #define SETTINGS_GROUP "Settings"
@@ -733,7 +734,7 @@ static gboolean update_operator_list(struct ofono_netreg *netreg, int total,
 			/* New operator */
 			struct network_operator_data *opd;
 
-			opd = g_memdup(copd,
+			opd = g_memdup2(copd,
 					sizeof(struct network_operator_data));
 
 			if (!network_operator_dbus_register(netreg, opd)) {
diff --git a/src/radio-settings.c b/src/radio-settings.c
index 7283aa9..a80e4c1 100644
--- a/src/radio-settings.c
+++ b/src/radio-settings.c
@@ -44,11 +44,11 @@ static GSList *g_drivers = NULL;
 struct ofono_radio_settings {
 	DBusMessage *pending;
 	int flags;
-	enum ofono_radio_access_mode mode;
+	unsigned int mode;
 	enum ofono_radio_band_gsm band_gsm;
 	enum ofono_radio_band_umts band_umts;
 	ofono_bool_t fast_dormancy;
-	enum ofono_radio_access_mode pending_mode;
+	unsigned int pending_mode;
 	enum ofono_radio_band_gsm pending_band_gsm;
 	enum ofono_radio_band_umts pending_band_umts;
 	ofono_bool_t fast_dormancy_pending;
@@ -60,7 +60,7 @@ struct ofono_radio_settings {
 	struct ofono_atom *atom;
 };
 
-static const char *radio_access_mode_to_string(enum ofono_radio_access_mode m)
+static const char *radio_access_mode_to_string(unsigned int m)
 {
 	switch (m) {
 	case OFONO_RADIO_ACCESS_MODE_ANY:
@@ -79,11 +79,14 @@ static const char *radio_access_mode_to_string(enum ofono_radio_access_mode m)
 	if (m == (OFONO_RADIO_ACCESS_MODE_LTE|OFONO_RADIO_ACCESS_MODE_UMTS))
 		return "lte,umts";
 
+	if (m == (OFONO_RADIO_ACCESS_MODE_LTE|OFONO_RADIO_ACCESS_MODE_GSM))
+		return "lte,gsm";
+
 	return NULL;
 }
 
 static gboolean radio_access_mode_from_string(const char *str,
-					enum ofono_radio_access_mode *mode)
+					unsigned int *mode)
 
 {
 	if (g_str_equal(str, "any")) {
@@ -104,6 +107,9 @@ static gboolean radio_access_mode_from_string(const char *str,
 	} else if (g_str_equal(str, "lte,umts")) {
 		*mode = OFONO_RADIO_ACCESS_MODE_LTE|OFONO_RADIO_ACCESS_MODE_UMTS;
 		return TRUE;
+	} else if (g_str_equal(str, "lte,gsm")) {
+		*mode = OFONO_RADIO_ACCESS_MODE_LTE|OFONO_RADIO_ACCESS_MODE_GSM;
+		return TRUE;
 	}
 
 	return FALSE;
@@ -373,7 +379,7 @@ static void radio_band_set_callback(const struct ofono_error *error,
 }
 
 static void radio_set_rat_mode(struct ofono_radio_settings *rs,
-				enum ofono_radio_access_mode mode)
+				unsigned int mode)
 {
 	DBusConnection *conn = ofono_dbus_get_connection();
 	const char *path;
@@ -522,8 +528,7 @@ static void radio_query_band(struct ofono_radio_settings *rs)
 }
 
 static void radio_rat_mode_query_callback(const struct ofono_error *error,
-					enum ofono_radio_access_mode mode,
-					void *data)
+						int mode, void *data)
 {
 	struct ofono_radio_settings *rs = data;
 	DBusMessage *reply;
@@ -588,7 +593,7 @@ static DBusMessage *radio_set_property(DBusConnection *conn, DBusMessage *msg,
 
 	if (g_strcmp0(property, "TechnologyPreference") == 0) {
 		const char *value;
-		enum ofono_radio_access_mode mode;
+		unsigned int mode;
 
 		if (rs->driver->set_rat_mode == NULL)
 			return __ofono_error_not_implemented(msg);
diff --git a/src/sim-auth.c b/src/sim-auth.c
index 0e13b53..6dab52e 100644
--- a/src/sim-auth.c
+++ b/src/sim-auth.c
@@ -207,14 +207,10 @@ static void handle_umts(struct ofono_sim_auth *sa, const uint8_t *resp,
 	DBusMessage *reply = NULL;
 	DBusMessageIter iter;
 	DBusMessageIter dict;
-	const uint8_t *res = NULL;
-	const uint8_t *ck = NULL;
-	const uint8_t *ik = NULL;
-	const uint8_t *auts = NULL;
-	const uint8_t *kc = NULL;
+	struct data_block res, ck, ik, auts, sres, kc;
 
 	if (!sim_parse_umts_authenticate(resp, len, &res, &ck, &ik,
-			&auts, &kc))
+			&auts, &sres, &kc))
 		goto umts_end;
 
 	reply = dbus_message_new_method_return(sa->pending->msg);
@@ -224,15 +220,23 @@ static void handle_umts(struct ofono_sim_auth *sa, const uint8_t *resp,
 	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
 			"{say}", &dict);
 
-	if (auts) {
-		append_dict_byte_array(&dict, "AUTS", auts, 14);
-	} else {
-		append_dict_byte_array(&dict, "RES", res, 8);
-		append_dict_byte_array(&dict, "CK", ck, 16);
-		append_dict_byte_array(&dict, "IK", ik, 16);
-		if (kc)
-			append_dict_byte_array(&dict, "Kc", kc, 8);
-	}
+	if (auts.data)
+		append_dict_byte_array(&dict, "AUTS", auts.data, auts.len);
+
+	if (sres.data)
+		append_dict_byte_array(&dict, "SRES", sres.data, sres.len);
+
+	if (res.data)
+		append_dict_byte_array(&dict, "RES", res.data, res.len);
+
+	if (ck.data)
+		append_dict_byte_array(&dict, "CK", ck.data, ck.len);
+
+	if (ik.data)
+		append_dict_byte_array(&dict, "IK", ik.data, ik.len);
+
+	if (kc.data)
+		append_dict_byte_array(&dict, "Kc", kc.data, kc.len);
 
 	dbus_message_iter_close_container(&iter, &dict);
 
@@ -367,6 +371,8 @@ static void get_session_cb(ofono_bool_t active, int session_id,
 error:
 	__ofono_dbus_pending_reply(&sa->pending->msg,
 			__ofono_error_failed(sa->pending->msg));
+	__ofono_sim_remove_session_watch(sa->pending->session,
+			sa->pending->watch_id);
 	g_free(sa->pending);
 	sa->pending = NULL;
 }
diff --git a/src/sim.c b/src/sim.c
index 535ccbc..0c4afef 100644
--- a/src/sim.c
+++ b/src/sim.c
@@ -47,6 +47,7 @@
 #include "storage.h"
 #include "simfs.h"
 #include "stkutil.h"
+#include "missing.h"
 
 /*
  * A new session object will be created if a USim/ISim applications are
@@ -1182,7 +1183,7 @@ static void sim_iidf_read_cb(int ok, int length, int record,
 		clut_len = data[3] * 3;
 
 	iidf_id = efimg[3] << 8 | efimg[4];
-	sim->iidf_image = g_memdup(data, length);
+	sim->iidf_image = g_memdup2(data, length);
 
 	/* The path it the same between 2G and 3G */
 	path_len = sim_ef_db_get_path_3g(SIM_EFIMG_FILEID, path);
@@ -1664,7 +1665,8 @@ static void impi_read_cb(int ok, int total_length, int record,
 		return;
 	}
 
-	sim->impi = g_strndup((const char *)data + 2, data[1]);
+	if (validate_utf8_tlv(data))
+		sim->impi = g_strndup((const char *)data + 2, data[1]);
 }
 
 static void discover_apps_cb(const struct ofono_error *error,
@@ -1924,7 +1926,7 @@ static void sim_efsst_read_cb(int ok, int length, int record,
 		goto out;
 	}
 
-	sim->efsst = g_memdup(data, length);
+	sim->efsst = g_memdup2(data, length);
 	sim->efsst_length = length;
 
 	/*
@@ -1963,7 +1965,7 @@ static void sim_efest_read_cb(int ok, int length, int record,
 		goto out;
 	}
 
-	sim->efest = g_memdup(data, length);
+	sim->efest = g_memdup2(data, length);
 	sim->efest_length = length;
 
 	/*
@@ -2007,7 +2009,7 @@ static void sim_efust_read_cb(int ok, int length, int record,
 		goto out;
 	}
 
-	sim->efust = g_memdup(data, length);
+	sim->efust = g_memdup2(data, length);
 	sim->efust_length = length;
 
 	/*
@@ -2150,7 +2152,7 @@ static void sim_efli_read_cb(int ok, int length, int record,
 	if (!ok)
 		return;
 
-	sim->efli = g_memdup(data, length);
+	sim->efli = g_memdup2(data, length);
 	sim->efli_length = length;
 }
 
@@ -3201,7 +3203,7 @@ static void sim_pin_query_cb(const struct ofono_error *error,
 	DBusConnection *conn = ofono_dbus_get_connection();
 	const char *path = __ofono_atom_get_path(sim->atom);
 	struct cached_pin *cpins = pin_cache_lookup(sim->iccid);
-	const char *pin_name = sim_passwd_name(pin_type);
+	const char *pin_name;
 	char **locked_pins;
 	gboolean lock_changed;
 
@@ -3212,6 +3214,8 @@ static void sim_pin_query_cb(const struct ofono_error *error,
 		return;
 	}
 
+	pin_name = sim_passwd_name(pin_type);
+
 	if (sim->pin_type != pin_type) {
 		sim->pin_type = pin_type;
 
@@ -3802,7 +3806,8 @@ void __ofono_sim_remove_session_watch(struct ofono_sim_aid_session *session,
 {
 	__ofono_watchlist_remove_item(session->watches, id);
 
-	if (g_slist_length(session->watches->items) == 0) {
+	if (g_slist_length(session->watches->items) == 0 &&
+			session->state == SESSION_STATE_OPEN) {
 		/* last watcher, close session */
 		session->state = SESSION_STATE_CLOSING;
 		session->sim->driver->close_channel(session->sim,
diff --git a/src/simfs.c b/src/simfs.c
index a42dee3..515f5c9 100644
--- a/src/simfs.c
+++ b/src/simfs.c
@@ -39,6 +39,7 @@
 #include "simfs.h"
 #include "simutil.h"
 #include "storage.h"
+#include "missing.h"
 
 #define SIM_CACHE_MODE 0600
 #define SIM_CACHE_BASEPATH STORAGEDIR "/%s-%i"
@@ -387,13 +388,12 @@ static void sim_fs_op_read_block_cb(const struct ofono_error *error,
 	if (op->current == start_block) {
 		bufoff = 0;
 		dataoff = op->offset % 256;
-		tocopy = MIN(256 - op->offset % 256,
-				op->num_bytes - op->current * 256);
+		tocopy = MIN(256 - dataoff, op->num_bytes);
 	} else {
-		bufoff = (op->current - start_block - 1) * 256 +
+		bufoff = (op->current - start_block) * 256 -
 				op->offset % 256;
 		dataoff = 0;
-		tocopy = MIN(256, op->num_bytes - op->current * 256);
+		tocopy = MIN(256, op->num_bytes - bufoff);
 	}
 
 	DBG("bufoff: %d, dataoff: %d, tocopy: %d",
@@ -462,13 +462,12 @@ static gboolean sim_fs_op_read_block(gpointer user_data)
 			bufoff = 0;
 			seekoff = SIM_CACHE_HEADER_SIZE + op->current * 256 +
 				op->offset % 256;
-			toread = MIN(256 - op->offset % 256,
-					op->num_bytes - op->current * 256);
+			toread = MIN(256 - op->offset % 256, op->num_bytes);
 		} else {
-			bufoff = (op->current - start_block - 1) * 256 +
+			bufoff = (op->current - start_block) * 256 -
 					op->offset % 256;
 			seekoff = SIM_CACHE_HEADER_SIZE + op->current * 256;
-			toread = MIN(256, op->num_bytes - op->current * 256);
+			toread = MIN(256, op->num_bytes - bufoff);
 		}
 
 		DBG("bufoff: %d, seekoff: %d, toread: %d",
@@ -1128,7 +1127,7 @@ int sim_fs_write(struct ofono_sim_context *context, int id,
 	op->cb = cb;
 	op->userdata = userdata;
 	op->is_read = FALSE;
-	op->buffer = g_memdup(data, length);
+	op->buffer = g_memdup2(data, length);
 	op->structure = structure;
 	op->length = length;
 	op->current = record;
diff --git a/src/simutil.c b/src/simutil.c
index 22dfaeb..59d8d5d 100644
--- a/src/simutil.c
+++ b/src/simutil.c
@@ -33,6 +33,7 @@
 #include "simutil.h"
 #include "util.h"
 #include "smsutil.h"
+#include "missing.h"
 
 struct sim_eons {
 	struct sim_eons_operator_info *pnn_list;
@@ -765,6 +766,20 @@ unsigned char *comprehension_tlv_builder_get_data(
 	return tlv + tag_size + len_size;
 }
 
+gboolean validate_utf8_tlv(const unsigned char *tlv)
+{
+	int len = tlv[1];
+
+	if (len == 0)
+		return FALSE;
+
+	/* support both null-terminated and non null-terminated TLV value */
+	if (tlv[len + 1] == '\0')
+		len -= 1;
+
+	return g_utf8_validate_len((const char *)tlv + 2, len, NULL);
+}
+
 static char *sim_network_name_parse(const unsigned char *buffer, int length,
 					gboolean *add_ci)
 {
@@ -1410,7 +1425,7 @@ gboolean sim_parse_3g_get_response(const unsigned char *data, int len,
 	if (tlv[1] != 0x21)
 		return FALSE;
 
-	switch (tlv[0] & 0x3) {
+	switch (tlv[0] & 0x7) {
 	case 1:	/* Transparent */
 		str = 0x00;
 		break;
@@ -1574,6 +1589,7 @@ GSList *sim_parse_app_template_entries(const unsigned char *buffer, int len)
 			goto error;
 
 		memcpy(app.aid, aid, app.aid_len);
+		memset(app.aid + app.aid_len, 0xff, 16 - app.aid_len);
 
 		app.type = (app.aid[5] << 8) | app.aid[6];
 
@@ -1592,7 +1608,7 @@ GSList *sim_parse_app_template_entries(const unsigned char *buffer, int len)
 		} else
 			app.label = NULL;
 
-		ret = g_slist_prepend(ret, g_memdup(&app, sizeof(app)));
+		ret = g_slist_prepend(ret, g_memdup2(&app, sizeof(app)));
 
 		len -= (dataobj - buffer) + dataobj_len;
 		buffer = dataobj + dataobj_len;
@@ -1658,67 +1674,135 @@ int sim_build_gsm_authenticate(unsigned char *buffer, int len,
 	return build_authenticate(buffer, rand, NULL);
 }
 
-gboolean sim_parse_umts_authenticate(const unsigned char *buffer,
-		int len, const unsigned char **res, const unsigned char **ck,
-		const unsigned char **ik, const unsigned char **auts,
-		const unsigned char **kc)
+gboolean sim_parse_umts_authenticate(const unsigned char *buffer, int len,
+		struct data_block *res, struct data_block *ck,
+		struct data_block *ik, struct data_block *auts,
+		struct data_block *sres, struct data_block *kc)
 {
-	if (len < 16 || !buffer)
+	const unsigned char *ptr = buffer;
+	const unsigned char *end = ptr + len;
+	unsigned int l;
+
+	if (!buffer || len < 2)
 		return FALSE;
 
-	switch (buffer[0]) {
+	memset(res, 0, sizeof(*res));
+	memset(ck, 0, sizeof(*ck));
+	memset(ik, 0, sizeof(*ik));
+	memset(kc, 0, sizeof(*kc));
+	memset(auts, 0, sizeof(*auts));
+	memset(sres, 0, sizeof(*sres));
+
+	/*
+	 * TS 31.102
+	 * 7.1.2.1 GSM/3G security context
+	 */
+	switch (*ptr++) {
 	case 0xdb:
-		/* 'DB' + '08' + RES(16) + '10' + CK(32) + '10' + IK(32) = 43 */
-		if (len < 43)
-			goto umts_end;
+		/*
+		 * Response parameters/data, case 1, 3G security context,
+		 * command successful:
+		 *
+		 * "Successful 3G authentication" tag = 'DB'
+		 * 'DB' + L3 + RES(L3) + L4 + CK(L4) + L5 + IK(L5) + 8 + Kc(8)
+		 */
+		l = *ptr++; /* L3 */
+		if ((ptr + l) > end)
+			return FALSE;
 
-		/* success */
-		if (buffer[1] != 0x08)
-			goto umts_end;
+		res->data = ptr;
+		res->len = l;
+		ptr += l;
 
-		*res = buffer + 2;
+		if (ptr == end)
+			return FALSE;
+
+		l = *ptr++; /* L4 */
+		if ((ptr + l) > end)
+			return FALSE;
 
-		if (buffer[10] != 0x10)
-			goto umts_end;
+		ck->data = ptr;
+		ck->len = l;
+		ptr += l;
 
-		*ck = buffer + 11;
+		if (ptr == end)
+			return FALSE;
 
-		if (buffer[27] != 0x10)
-			goto umts_end;
+		l = *ptr++; /* L5 */
+		if ((ptr + l) > end)
+			return FALSE;
 
-		*ik = buffer + 28;
+		ik->data = ptr;
+		ik->len = l;
+		ptr += l;
 
-		if (len >= 53 && kc) {
-			if (buffer[44] != 0x08)
-				goto umts_end;
+		if (ptr < end) {
+			l = *ptr++;
+			if (l != 8 || (ptr + l) != end)
+				return FALSE;
 
-			*kc = buffer + 45;
-		} else {
-			*kc = NULL;
+			kc->data = ptr;
+			kc->len = l;
+			ptr += l;
 		}
 
-		*auts = NULL;
+		return TRUE;
 
-		break;
 	case 0xdc:
-		/* 'DC' + '0E' + AUTS(14) = 16 */
-		if (len < 16)
-			goto umts_end;
+		/*
+		 * Response parameters/data, case 2, 3G security context,
+		 * synchronisation failure:
+		 *
+		 * "Synchronisation failure" tag = 'DC'
+		 * 'DC' + L1 + AUTS(L1)
+		 */
+		l = *ptr++; /* L1 */
+		if ((ptr + l) > end)
+			return FALSE;
 
-		/* sync error */
-		if (buffer[1] != 0x0e)
-			goto umts_end;
+		auts->data = ptr;
+		auts->len = l;
+		ptr += l;
 
-		*auts = buffer + 2;
+		if (ptr != end)
+			return FALSE;
 
-		break;
-	default:
-		goto umts_end;
-	}
+		return TRUE;
 
-	return TRUE;
+	case 0x04:
+		/*
+		 * Response parameters/data, case 3, GSM security context,
+		 * command successful:
+		 *
+		 * 4 + SRES(4) + 8 + Kc(8)
+		 */
+		l = 4; /* Already skipped this one */
+		if ((ptr + l) > end)
+			return FALSE;
+
+		sres->data = ptr;
+		sres->len = l;
+		ptr += l;
+
+		if (ptr == end)
+			return FALSE;
 
-umts_end:
+		l = *ptr++; /* 8 */
+		if (l != 8 || (ptr + l) > end)
+			return FALSE;
+
+		kc->data = ptr;
+		kc->len = l;
+		ptr += l;
+
+		if (ptr != end)
+			return FALSE;
+
+		return TRUE;
+
+	default:
+		break;
+	}
 	return FALSE;
 }
 
diff --git a/src/simutil.h b/src/simutil.h
index 14a3995..f908bbb 100644
--- a/src/simutil.h
+++ b/src/simutil.h
@@ -371,6 +371,11 @@ struct comprehension_tlv_builder {
 	struct ber_tlv_builder *parent;
 };
 
+struct data_block {
+	const unsigned char *data;
+	unsigned int len;
+};
+
 void simple_tlv_iter_init(struct simple_tlv_iter *iter,
 				const unsigned char *pdu, unsigned int len);
 gboolean simple_tlv_iter_next(struct simple_tlv_iter *iter);
@@ -403,6 +408,7 @@ gboolean comprehension_tlv_builder_set_length(
 				unsigned int len);
 unsigned char *comprehension_tlv_builder_get_data(
 				struct comprehension_tlv_builder *builder);
+gboolean validate_utf8_tlv(const unsigned char *data);
 
 void ber_tlv_iter_init(struct ber_tlv_iter *iter, const unsigned char *pdu,
 			unsigned int len);
@@ -526,10 +532,10 @@ int sim_build_umts_authenticate(unsigned char *buffer, int len,
 int sim_build_gsm_authenticate(unsigned char *buffer, int len,
 		const unsigned char *rand);
 
-gboolean sim_parse_umts_authenticate(const unsigned char *buffer,
-		int len, const unsigned char **res, const unsigned char **ck,
-		const unsigned char **ik, const unsigned char **auts,
-		const unsigned char **kc);
+gboolean sim_parse_umts_authenticate(const unsigned char *buffer, int len,
+		struct data_block *res, struct data_block *ck,
+		struct data_block *ik, struct data_block *auts,
+		struct data_block *sres, struct data_block *kc);
 
 gboolean sim_parse_gsm_authenticate(const unsigned char *buffer, int len,
 		const unsigned char **sres, const unsigned char **kc);
diff --git a/src/smsutil.c b/src/smsutil.c
index 8c084d4..8e57a06 100644
--- a/src/smsutil.c
+++ b/src/smsutil.c
@@ -38,6 +38,7 @@
 #include "util.h"
 #include "storage.h"
 #include "smsutil.h"
+#include "missing.h"
 
 #define uninitialized_var(x) x = x
 
@@ -1756,7 +1757,7 @@ gboolean sms_udh_iter_init_from_cbs(const struct cbs *cbs,
 		return FALSE;
 
 	hdr = cbs->ud;
-	max_ud_len = 82;
+	max_ud_len = cbs->udlen;
 
 	/* Must have at least one information-element if udhi is true */
 	if (hdr[0] < 2)
@@ -2814,7 +2815,7 @@ static void sr_assembly_load_backup(GHashTable *assembly_table,
 	}
 
 	/* Node ready, create key and add them to the table */
-	id_table_key = g_memdup(msgid, SMS_MSGID_LEN);
+	id_table_key = g_memdup2(msgid, SMS_MSGID_LEN);
 
 	g_hash_table_insert(id_table, id_table_key, node);
 }
@@ -3129,7 +3130,7 @@ void status_report_assembly_add_fragment(
 
 	/* Create node in the message id hashtable if required */
 	if (node == NULL) {
-		id_table_key = g_memdup(msgid, SMS_MSGID_LEN);
+		id_table_key = g_memdup2(msgid, SMS_MSGID_LEN);
 
 		node = g_new0(struct id_table_node, 1);
 		node->total_mrs = total_mrs;
@@ -3240,7 +3241,7 @@ static GSList *sms_tx_load(const char *imsi, const struct dirent *dir)
 		if (sms_deserialize_outgoing(buf, &s, r) == FALSE)
 			goto free_pdu;
 
-		list = g_slist_prepend(list, g_memdup(&s, sizeof(s)));
+		list = g_slist_prepend(list, g_memdup2(&s, sizeof(s)));
 
 free_pdu:
 		g_free(pdus[len]);
@@ -3575,6 +3576,7 @@ GSList *sms_text_prepare_with_alphabet(const char *to, const char *utf8,
 	GSList *r = NULL;
 	enum gsm_dialect used_locking;
 	enum gsm_dialect used_single;
+	enum gsm_dialect dialect;
 
 	memset(&template, 0, sizeof(struct sms));
 	template.type = SMS_TYPE_SUBMIT;
@@ -3586,12 +3588,14 @@ GSList *sms_text_prepare_with_alphabet(const char *to, const char *utf8,
 	template.submit.vp.relative = 0xA7; /* 24 Hours */
 	sms_address_from_string(&template.submit.daddr, to);
 
+	/* There are two enums for the same thing */
+	dialect = (enum gsm_dialect)alphabet;
 	/*
 	 * UDHI, UDL, UD and DCS actually depend on the contents of
 	 * the text, and also on the GSM dialect we use to encode it.
 	 */
 	gsm_encoded = convert_utf8_to_gsm_best_lang(utf8, -1, NULL, &written, 0,
-							alphabet, &used_locking,
+							dialect, &used_locking,
 							&used_single);
 	if (!gsm_encoded) {
 		size_t converted;
@@ -3862,8 +3866,8 @@ gboolean cbs_dcs_decode(guint8 dcs, gboolean *udhi, enum sms_class *cls,
 
 gboolean cbs_decode(const unsigned char *pdu, int len, struct cbs *out)
 {
-	/* CBS is always a fixed length of 88 bytes */
-	if (len != 88)
+	/* CBS is (almost) always a fixed length of 88 bytes */
+	if (len < 6 || len > 88)
 		return FALSE;
 
 	out->gs = (enum cbs_geo_scope) ((pdu[0] >> 6) & 0x03);
@@ -3874,6 +3878,10 @@ gboolean cbs_decode(const unsigned char *pdu, int len, struct cbs *out)
 	out->max_pages = pdu[5] & 0xf;
 	out->page = (pdu[5] >> 4) & 0xf;
 
+	/* Allow the last fragment to be truncated */
+	if (len != 88 && out->max_pages != out->page)
+		return FALSE;
+
 	/*
 	 * If a mobile receives the code 0000 in either the first field or
 	 * the second field then it shall treat the CBS message exactly the
@@ -3885,7 +3893,10 @@ gboolean cbs_decode(const unsigned char *pdu, int len, struct cbs *out)
 		out->page = 1;
 	}
 
-	memcpy(out->ud, pdu + 6, 82);
+	out->udlen = (guint8)(len - 6);
+	memcpy(out->ud, pdu + 6, out->udlen);
+	if (out->udlen < 82)
+		memset(out->ud + out->udlen, 0, 82 - out->udlen);
 
 	return TRUE;
 }
@@ -4078,7 +4089,7 @@ char *cbs_decode_text(GSList *cbs_list, char *iso639_lang)
 			if (iso639)
 				bufsize -= 3;
 		} else {
-			bufsize += 82;
+			bufsize += cbs->udlen;
 
 			if (iso639)
 				bufsize -= 2;
@@ -4095,7 +4106,7 @@ char *cbs_decode_text(GSList *cbs_list, char *iso639_lang)
 			if (sms_udh_iter_init_from_cbs(cbs, &iter))
 				taken = sms_udh_iter_get_udh_length(&iter) + 1;
 
-			unpack_7bit_own_buf(cbs->ud + taken, 82 - taken,
+			unpack_7bit_own_buf(cbs->ud + taken, cbs->udlen - taken,
 						taken, false, 2,
 						NULL, 0,
 						(unsigned char *)iso639_lang);
@@ -4128,7 +4139,7 @@ char *cbs_decode_text(GSList *cbs_list, char *iso639_lang)
 			max_chars =
 				sms_text_capacity_gsm(CBS_MAX_GSM_CHARS, taken);
 
-			unpack_7bit_own_buf(ud + taken, 82 - taken,
+			unpack_7bit_own_buf(ud + taken, cbs->udlen - taken,
 						taken, false, max_chars,
 						&written, 0, unpacked);
 
@@ -4162,7 +4173,7 @@ char *cbs_decode_text(GSList *cbs_list, char *iso639_lang)
 			 * the check here since the specification isn't clear
 			 */
 		} else {
-			int num_ucs2_chars = (82 - taken) >> 1;
+			int num_ucs2_chars = (cbs->udlen - taken) >> 1;
 			int i = taken;
 			int max_offset = taken + num_ucs2_chars * 2;
 
diff --git a/src/smsutil.h b/src/smsutil.h
index 0adba51..01487de 100644
--- a/src/smsutil.h
+++ b/src/smsutil.h
@@ -408,6 +408,7 @@ struct cbs {
 	guint8 dcs;				/* 8 bits */
 	guint8 max_pages;			/* 4 bits */
 	guint8 page;				/* 4 bits */
+	guint8 udlen;
 	guint8 ud[82];
 };
 
diff --git a/src/voicecall.c b/src/voicecall.c
index 93a9254..3da258d 100644
--- a/src/voicecall.c
+++ b/src/voicecall.c
@@ -38,6 +38,7 @@
 #include "simutil.h"
 #include "smsutil.h"
 #include "storage.h"
+#include "missing.h"
 
 #define MAX_VOICE_CALLS 16
 
@@ -2417,7 +2418,7 @@ void ofono_voicecall_notify(struct ofono_voicecall *vc,
 
 	__ofono_modem_callid_hold(modem, call->id);
 
-	newcall = g_memdup(call, sizeof(struct ofono_call));
+	newcall = g_memdup2(call, sizeof(struct ofono_call));
 	if (newcall == NULL) {
 		ofono_error("Unable to allocate call");
 		goto error;
diff --git a/test/set-sms-alphabet b/test/set-sms-alphabet
old mode 100644
new mode 100755
diff --git a/test/test-serving-cell-info b/test/test-serving-cell-info
old mode 100644
new mode 100755
diff --git a/tools/stktest.c b/tools/stktest.c
index c83d483..80d277e 100644
--- a/tools/stktest.c
+++ b/tools/stktest.c
@@ -36,6 +36,7 @@
 #include <gdbus.h>
 #include <gatchat/gatserver.h>
 
+#include "src/missing.h"
 #include "unit/stk-test-data.h"
 
 #define OFONO_SERVICE	"org.ofono"
@@ -3375,9 +3376,9 @@ static void stktest_add_test(const char *name, const char *method,
 
 	test->name = g_strdup(name);
 	test->method = g_strdup(method);
-	test->req_pdu = g_memdup(req, req_len);
+	test->req_pdu = g_memdup2(req, req_len);
 	test->req_len = req_len;
-	test->rsp_pdu = g_memdup(rsp, rsp_len);
+	test->rsp_pdu = g_memdup2(rsp, rsp_len);
 	test->rsp_len = rsp_len;
 	test->agent_func = agent_func;
 	test->tr_func = tr_func;
diff --git a/unit/test-simutil.c b/unit/test-simutil.c
index 083bd4b..530f624 100644
--- a/unit/test-simutil.c
+++ b/unit/test-simutil.c
@@ -86,6 +86,27 @@ static void test_ber_tlv_iter(void)
 	test_buffer(valid_mms_params, sizeof(valid_mms_params));
 }
 
+static void test_validate_tlv(void)
+{
+	unsigned char impi_none[] = { 0x80, 0x0 };
+	unsigned char impi_empty[] = { 0x80, 0x1, '\0' };
+	unsigned char impi_term1[] = { 0x80, 0x3, 'F', 'O', 'O' };
+	unsigned char impi_term2[] = { 0x80, 0x4, 'F', 'O', 'O', '\0' };
+	unsigned char impi_term3[] = { 0x80, 0x3, 'F', 'O', 'O', 0xff, 0xff };
+	unsigned char impi_term4[] = { 0x80, 0x4, 'F', 'O', 'O', '\0', 0xff };
+	unsigned char impi_invalid1[] = { 0x80, 0x4, 'F', '\0', 'O', '\0' };
+	unsigned char impi_invalid2[] = { 0x80, 0x4, 0xff, 0xff, 0xff, 0xff };
+
+	g_assert(validate_utf8_tlv(impi_none) == FALSE);
+	g_assert(validate_utf8_tlv(impi_empty) == TRUE);
+	g_assert(validate_utf8_tlv(impi_term1) == TRUE);
+	g_assert(validate_utf8_tlv(impi_term2) == TRUE);
+	g_assert(validate_utf8_tlv(impi_term3) == TRUE);
+	g_assert(validate_utf8_tlv(impi_term4) == TRUE);
+	g_assert(validate_utf8_tlv(impi_invalid1) == FALSE);
+	g_assert(validate_utf8_tlv(impi_invalid2) == FALSE);
+}
+
 static void test_ber_tlv_builder_mms(void)
 {
 	struct ber_tlv_iter top_iter, nested_iter;
@@ -501,32 +522,34 @@ static void test_get_2g_path(void)
 static void test_auth_build_parse(void)
 {
 	unsigned char auth_cmd[40];
-	const unsigned char rand[16] = { 0x00, 0x01, 0x02, 0x03, 0x04,0x05,
+	const unsigned char rand[] = { 0x00, 0x01, 0x02, 0x03, 0x04,0x05,
 			0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
 			0x0e, 0x0f };
-	const unsigned char sres[4] = { 0x00, 0x11, 0x22, 0x33 };
+	const unsigned char sres[] = { 0x00, 0x11, 0x22, 0x33 };
 	const unsigned char *sres_p;
+	struct data_block sres_b;
 	const unsigned char kc[8] = { 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56,
 			0x78, 0x9a };
 	const unsigned char *kc_p;
+	struct data_block kc_b;
 	const unsigned char gsm_success[] = { 0x04, 0x00, 0x11, 0x22, 0x33,
 			0x08,0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x9a };
-	const unsigned char autn[16] = { 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a,
+	const unsigned char autn[] = { 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a,
 			0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
 			0x01, 0x00 };
-	const unsigned char res[8] = { 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa,
+	const unsigned char res[] = { 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa,
 			0x11, 0x22 };
-	const unsigned char *res_p;
-	const unsigned char ck[16] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+	struct data_block res_b;
+	const unsigned char ck[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
 			0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
-	const unsigned char *ck_p;
-	const unsigned char ik[16] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd,
+	struct data_block ck_b;
+	const unsigned char ik[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd,
 			0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 };
-	const unsigned char *ik_p;
-	const unsigned char auts[16] = { 0xde, 0xea, 0xbe, 0xef, 0xde, 0xea,
-			0xbe, 0xef, 0xde, 0xea, 0xbe, 0xef, 0xde, 0xea,
-			0xbe, 0xef };
-	const unsigned char *auts_p;
+	struct data_block ik_b;
+	const unsigned char auts[] = { 0xde, 0xea,
+			0xbe, 0xef, 0xde, 0xea, 0xbe, 0xef, 0xde, 0xea, 0xbe,
+			0xef, 0xde, 0xea };
+	struct data_block auts_b;
 
 	const unsigned char umts_success[] = { 0xdb, 0x08, 0xff, 0xee, 0xdd,
 			0xcc, 0xbb, 0xaa, 0x11, 0x22, 0x10, 0x00, 0x11, 0x22,
@@ -544,6 +567,8 @@ static void test_auth_build_parse(void)
 	const unsigned char umts_sync_failure[] = { 0xdc, 0x0e, 0xde, 0xea,
 			0xbe, 0xef, 0xde, 0xea, 0xbe, 0xef, 0xde, 0xea, 0xbe,
 			0xef, 0xde, 0xea };
+	const unsigned char case3[] = { 0x04, 0x01, 0x02, 0x03, 0x04,
+			0x08, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x9a };
 	int len = 0;
 
 	/* test GSM auth command */
@@ -578,31 +603,96 @@ static void test_auth_build_parse(void)
 	g_assert(!memcmp(sres_p, sres, 4));
 	g_assert(!memcmp(kc_p, kc, 8));
 
+	/* test truncated messages */
+	g_assert(!sim_parse_umts_authenticate(umts_success, 1,
+			&res_b, &ck_b, &ik_b, &auts_b, &sres_b, &kc_b));
+	g_assert(!sim_parse_umts_authenticate(umts_success, 2,
+			&res_b, &ck_b, &ik_b, &auts_b, &sres_b, &kc_b));
+	g_assert(!sim_parse_umts_authenticate(umts_success, 10,
+			&res_b, &ck_b, &ik_b, &auts_b, &sres_b, &kc_b));
+	g_assert(!sim_parse_umts_authenticate(umts_success, 11,
+			&res_b, &ck_b, &ik_b, &auts_b, &sres_b, &kc_b));
+	g_assert(!sim_parse_umts_authenticate(umts_success, 27,
+			&res_b, &ck_b, &ik_b, &auts_b, &sres_b, &kc_b));
+	g_assert(!sim_parse_umts_authenticate(umts_success, 28,
+			&res_b, &ck_b, &ik_b, &auts_b, &sres_b, &kc_b));
+	g_assert(!sim_parse_umts_authenticate(umts_sync_failure, 2,
+			&res_b, &ck_b, &ik_b, &auts_b, &sres_b, &kc_b));
+	g_assert(!sim_parse_umts_authenticate(case3, 2,
+			&res_b, &ck_b, &ik_b, &auts_b, &sres_b, &kc_b));
+	g_assert(!sim_parse_umts_authenticate(case3, 5,
+			&res_b, &ck_b, &ik_b, &auts_b, &sres_b, &kc_b));
+	g_assert(!sim_parse_umts_authenticate(case3, 6,
+			&res_b, &ck_b, &ik_b, &auts_b, &sres_b, &kc_b));
+
+	/* the extra byte won't actually be accessed */
+	g_assert(!sim_parse_umts_authenticate(umts_success,
+			sizeof(umts_success) + 1,
+			&res_b, &ck_b, &ik_b, &auts_b, &sres_b, &kc_b));
+	g_assert(!sim_parse_umts_authenticate(umts_sync_failure,
+			sizeof(umts_sync_failure) + 1,
+			&res_b, &ck_b, &ik_b, &auts_b, &sres_b, &kc_b));
+	g_assert(!sim_parse_umts_authenticate(case3, sizeof(case3) + 1,
+			&res_b, &ck_b, &ik_b, &auts_b, &sres_b, &kc_b));
+
+	/* unrecognized data */
+	g_assert(!sim_parse_umts_authenticate(case3 + 1, sizeof(case3) - 1,
+			&res_b, &ck_b, &ik_b, &auts_b, &sres_b, &kc_b));
+
 	/* test UMTS success parse, no kc */
 	g_assert(sim_parse_umts_authenticate(umts_success, sizeof(umts_success),
-			&res_p, &ck_p, &ik_p, &auts_p, &kc_p));
-	g_assert(!memcmp(res_p, res, 8));
-	g_assert(!memcmp(ck_p, ck, 16));
-	g_assert(!memcmp(ik_p, ik, 16));
-	g_assert(!auts_p && !kc_p);
+			&res_b, &ck_b, &ik_b, &auts_b, &sres_b, &kc_b));
+	g_assert_cmpuint(res_b.len, == , sizeof(res));
+	g_assert(!memcmp(res_b.data, res, sizeof(res)));
+	g_assert_cmpuint(ck_b.len, == , sizeof(ck));
+	g_assert(!memcmp(ck_b.data, ck, sizeof(ck)));
+	g_assert_cmpuint(ik_b.len, == , sizeof(ik));
+	g_assert(!memcmp(ik_b.data, ik, sizeof(ik)));
+	g_assert(!sres_b.len && !sres_b.data);
+	g_assert(!auts_b.len && !auts_b.data);
+	g_assert(!kc_b.len && !kc_b.data);
 
 	/* test UMTS sync failure */
 	g_assert(sim_parse_umts_authenticate(umts_sync_failure,
 						sizeof(umts_sync_failure),
-						&res_p, &ck_p, &ik_p, &auts_p,
-						&kc_p));
-	g_assert(!memcmp(auts_p, auts, 14));
+						&res_b, &ck_b, &ik_b, &auts_b,
+						&sres_b, &kc_b));
+	g_assert_cmpuint(auts_b.len, == , sizeof(auts));
+	g_assert(!memcmp(auts_b.data, auts, sizeof(auts)));
+	g_assert(!res_b.len && !res_b.data);
+	g_assert(!ck_b.len && !ck_b.data);
+	g_assert(!ik_b.len && !ik_b.data);
+	g_assert(!sres_b.len && !sres_b.data);
+	g_assert(!kc_b.len && !kc_b.data);
 
 	/* test UMTS success parse, with kc */
 	g_assert(sim_parse_umts_authenticate(umts_success_kc,
 						sizeof(umts_success_kc),
-						&res_p, &ck_p, &ik_p, &auts_p,
-						&kc_p));
-	g_assert(!memcmp(res_p, res, 8));
-	g_assert(!memcmp(ck_p, ck, 16));
-	g_assert(!memcmp(ik_p, ik, 16));
-	g_assert(!memcmp(kc_p, kc, 8));
-	g_assert(!auts_p);
+						&res_b, &ck_b, &ik_b, &auts_b,
+						&sres_b, &kc_b));
+	g_assert_cmpuint(res_b.len, == , sizeof(res));
+	g_assert(!memcmp(res_b.data, res, sizeof(res)));
+	g_assert_cmpuint(ck_b.len, == , sizeof(ck));
+	g_assert(!memcmp(ck_b.data, ck, sizeof(ck)));
+	g_assert_cmpuint(ik_b.len, == , sizeof(ik));
+	g_assert(!memcmp(ik_b.data, ik, sizeof(ik)));
+	g_assert_cmpuint(kc_b.len, == , sizeof(kc));
+	g_assert(!memcmp(kc_b.data, kc, sizeof(kc)));
+	g_assert(!sres_b.len && !sres_b.data);
+	g_assert(!auts_b.len && !auts_b.data);
+
+	/* test case3 */
+	g_assert(sim_parse_umts_authenticate(case3, sizeof(case3),
+						&res_b, &ck_b, &ik_b, &auts_b,
+						&sres_b, &kc_b));
+	g_assert(!res_b.len && !res_b.data);
+	g_assert(!ck_b.len && !ck_b.data);
+	g_assert(!ik_b.len && !ik_b.data);
+	g_assert(!auts_b.len && !auts_b.data);
+	g_assert_cmpuint(sres_b.len, == , 4);
+	g_assert(!memcmp(sres_b.data, case3 + 1, sres_b.len));
+	g_assert_cmpuint(kc_b.len, == , sizeof(kc));
+	g_assert(!memcmp(kc_b.data, kc, sizeof(kc)));
 }
 
 int main(int argc, char **argv)
@@ -610,6 +700,7 @@ int main(int argc, char **argv)
 	g_test_init(&argc, &argv, NULL);
 
 	g_test_add_func("/testsimutil/ber tlv iter", test_ber_tlv_iter);
+	g_test_add_func("/testsimutil/ber tlv validate utf8", test_validate_tlv);
 	g_test_add_func("/testsimutil/ber tlv encode MMS",
 			test_ber_tlv_builder_mms);
 	g_test_add_func("/testsimutil/ber tlv encode EFpnn",