[meego-commits] 8935: Changes to Trunk/ofono

Peter Zhu no_reply at build.meego.com
Wed Oct 27 11:36:53 UTC 2010


Hi,
I have made the following changes to ofono in project Trunk. Please review and accept ASAP.

Thank You,
Peter Zhu

[This message was auto-generated]

---

Request #8935:

  submit:   Trunk:Testing/ofono(r13) -> Trunk/ofono


Message:
    Move to Trunk

State:   new          2010-10-27T04:36:52 peter
Comment: None



changes files:
--------------
--- ofono.changes
+++ ofono.changes
@@ -0,0 +1,3 @@
+* Tue Oct 26 2010 Martin Xu <martin.xu at intel.com> - 0.33
+- upgrade to 0.33 to fix IFX-MAL bugs
+

old:
----
  ofono-0.31.tar.gz

new:
----
  ofono-0.33.tar.gz

spec files:
-----------
--- ofono.spec
+++ ofono.spec
@@ -1,13 +1,13 @@
 # 
-# Do NOT Edit the Auto-generated Part!
-# Generated by: spectacle version 0.18
+# Do not Edit! Generated by:
+# spectacle version 0.18
 # 
 # >> macros
 # << macros
 
 Name:       ofono
 Summary:    Open Source Telephony
-Version:    0.31
+Version:    0.33
 Release:    1
 Group:      System/Networking
 License:    GPLv2

other changes:
--------------

++++++ ofono-0.31.tar.gz -> ofono-0.33.tar.gz
--- ChangeLog
+++ ChangeLog
@@ -1,3 +1,27 @@
+ver 0.33:
+	Fix wrong string to enum mapping of radio settings.
+	Fix issue with MMI code to bearer class mappings.
+	Fix issue with setting correct phase from EFphase.
+	Fix issue with phonebook handling and Infineon modems.
+	Fix issue with STK session end handling and Infineon modems.
+	Fix issue with SMS handling and ISI modems.
+	Fix issue with setting SCA type and ISI modems.
+	Add support for FastDormancy property.
+	Add support for FixedDialing property to indicate FDN.
+	Add support for Infineon specific M-RAW_IP GPRS context.
+	Add support for handling Send DTMF proactive command.
+	Add support for handling SIM Toolkit text attributes.
+
+ver 0.32:
+	Fix issue with AT+VTS not using quotes.
+	Fix issue with entering PUK and Infineon modems.
+	Fix issue with SIM hotswap and Infineon modems.
+	Fix issue with hangup active and ISI modems.
+	Fix issue with logic to validate USSD strings.
+	Add support for call in progress logic to USSD handling.
+	Add support for detecting FDN enabled SIM cards.
+	Add support for accessing SIM icon storage.
+
 ver 0.31:
 	Fix issue with signal strength reporting for ISI modems.
 	Fix issue with GPRS detach reporting for ISI modems.
--- Makefile.am
+++ Makefile.am
@@ -26,6 +26,12 @@
 
 dbusconf_DATA = src/ofono.conf
 
+if SYSTEMD
+systemdunitdir = @SYSTEMD_UNITDIR@
+
+systemdunit_DATA = src/ofono.service
+endif
+
 confdir = $(sysconfdir)/ofono
 
 conf_DATA =
@@ -65,6 +71,7 @@
 				gatchat/gatutil.h gatchat/gatutil.c \
 				gatchat/gat.h \
 				gatchat/gatserver.h gatchat/gatserver.c \
+				gatchat/gatrawip.h gatchat/gatrawip.c \
 				gatchat/gathdlc.c gatchat/gathdlc.h \
 				gatchat/gatppp.c gatchat/gatppp.h \
 				gatchat/ppp.h gatchat/ppp_cp.h \
@@ -217,6 +224,7 @@
 			drivers/ifxmodem/voicecall.c \
 			drivers/ifxmodem/audio-settings.c \
 			drivers/ifxmodem/radio-settings.c \
+			drivers/ifxmodem/gprs-context.c \
 			drivers/ifxmodem/stk.c
 
 builtin_modules += stemodem
@@ -362,6 +370,7 @@
 		test/enter-pin \
 		test/hangup-all \
 		test/hangup-active \
+		test/set-roaming-allowed \
 		test/set-context \
 		test/list-contexts \
 		test/list-modems \
@@ -400,7 +409,8 @@
 		test/lock-pin \
 		test/unlock-pin \
 		test/enable-gprs \
-		test/disable-gprs
+		test/disable-gprs \
+		test/get-icon
 
 if TEST
 testdir = $(pkglibdir)/test
--- Makefile.in
+++ Makefile.in
@@ -134,6 +134,7 @@
 @ATMODEM_TRUE@	drivers/ifxmodem/voicecall.c \
 @ATMODEM_TRUE@	drivers/ifxmodem/audio-settings.c \
 @ATMODEM_TRUE@	drivers/ifxmodem/radio-settings.c \
+ at ATMODEM_TRUE@	drivers/ifxmodem/gprs-context.c \
 @ATMODEM_TRUE@	drivers/ifxmodem/stk.c drivers/atmodem/atutil.h \
 @ATMODEM_TRUE@	drivers/stemodem/stemodem.h \
 @ATMODEM_TRUE@	drivers/stemodem/stemodem.c \
@@ -163,7 +164,8 @@
 DIST_COMMON = README $(am__configure_deps) $(dist_man_MANS) \
 	$(include_HEADERS) $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
 	$(srcdir)/config.h.in $(top_srcdir)/configure \
-	$(top_srcdir)/include/version.h.in AUTHORS COPYING ChangeLog \
+	$(top_srcdir)/include/version.h.in \
+	$(top_srcdir)/src/ofono.service.in AUTHORS COPYING ChangeLog \
 	INSTALL NEWS TODO compile config.guess config.sub depcomp \
 	install-sh ltmain.sh missing
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
@@ -175,13 +177,13 @@
  configure.lineno config.status.lineno
 mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = config.h
-CONFIG_CLEAN_FILES = include/version.h
+CONFIG_CLEAN_FILES = include/version.h src/ofono.service
 CONFIG_CLEAN_VPATH_FILES =
 am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(testdir)" \
 	"$(DESTDIR)$(man8dir)" "$(DESTDIR)$(confdir)" \
 	"$(DESTDIR)$(dbusconfdir)" "$(DESTDIR)$(rulesdir)" \
-	"$(DESTDIR)$(statedir)" "$(DESTDIR)$(includedir)" \
-	"$(DESTDIR)$(includedir)"
+	"$(DESTDIR)$(statedir)" "$(DESTDIR)$(systemdunitdir)" \
+	"$(DESTDIR)$(includedir)" "$(DESTDIR)$(includedir)"
 PROGRAMS = $(noinst_PROGRAMS) $(sbin_PROGRAMS)
 am__dirstamp = $(am__leading_dot)dirstamp
 am__objects_1 = gatchat/gatchat.$(OBJEXT) gatchat/gatresult.$(OBJEXT) \
@@ -189,10 +191,11 @@
 	gatchat/gatio.$(OBJEXT) gatchat/crc-ccitt.$(OBJEXT) \
 	gatchat/gatmux.$(OBJEXT) gatchat/gsm0710.$(OBJEXT) \
 	gatchat/gattty.$(OBJEXT) gatchat/gatutil.$(OBJEXT) \
-	gatchat/gatserver.$(OBJEXT) gatchat/gathdlc.$(OBJEXT) \
-	gatchat/gatppp.$(OBJEXT) gatchat/ppp_cp.$(OBJEXT) \
-	gatchat/ppp_lcp.$(OBJEXT) gatchat/ppp_auth.$(OBJEXT) \
-	gatchat/ppp_net.$(OBJEXT) gatchat/ppp_ipcp.$(OBJEXT)
+	gatchat/gatserver.$(OBJEXT) gatchat/gatrawip.$(OBJEXT) \
+	gatchat/gathdlc.$(OBJEXT) gatchat/gatppp.$(OBJEXT) \
+	gatchat/ppp_cp.$(OBJEXT) gatchat/ppp_lcp.$(OBJEXT) \
+	gatchat/ppp_auth.$(OBJEXT) gatchat/ppp_net.$(OBJEXT) \
+	gatchat/ppp_ipcp.$(OBJEXT)
 am_gatchat_gsmdial_OBJECTS = gatchat/gsmdial.$(OBJEXT) \
 	$(am__objects_1)
 gatchat_gsmdial_OBJECTS = $(am_gatchat_gsmdial_OBJECTS)
@@ -241,14 +244,15 @@
 	gatchat/gatmux.h gatchat/gatmux.c gatchat/gsm0710.h \
 	gatchat/gsm0710.c gatchat/gattty.h gatchat/gattty.c \
 	gatchat/gatutil.h gatchat/gatutil.c gatchat/gat.h \
-	gatchat/gatserver.h gatchat/gatserver.c gatchat/gathdlc.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 drivers/atmodem/atmodem.h \
-	drivers/atmodem/atmodem.c drivers/atmodem/call-settings.c \
-	drivers/atmodem/sms.c drivers/atmodem/cbs.c \
-	drivers/atmodem/call-forwarding.c drivers/atmodem/call-meter.c \
+	gatchat/gatserver.h gatchat/gatserver.c gatchat/gatrawip.h \
+	gatchat/gatrawip.c gatchat/gathdlc.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 \
+	drivers/atmodem/atmodem.h drivers/atmodem/atmodem.c \
+	drivers/atmodem/call-settings.c drivers/atmodem/sms.c \
+	drivers/atmodem/cbs.c drivers/atmodem/call-forwarding.c \
+	drivers/atmodem/call-meter.c \
 	drivers/atmodem/network-registration.c drivers/atmodem/sim.c \
 	drivers/atmodem/stk.c drivers/atmodem/stk.h \
 	drivers/atmodem/sim-poll.c drivers/atmodem/sim-poll.h \
@@ -278,7 +282,8 @@
 	drivers/hsomodem/radio-settings.c drivers/ifxmodem/ifxmodem.h \
 	drivers/ifxmodem/ifxmodem.c drivers/ifxmodem/voicecall.c \
 	drivers/ifxmodem/audio-settings.c \
-	drivers/ifxmodem/radio-settings.c drivers/ifxmodem/stk.c \
+	drivers/ifxmodem/radio-settings.c \
+	drivers/ifxmodem/gprs-context.c drivers/ifxmodem/stk.c \
 	drivers/stemodem/stemodem.h drivers/stemodem/stemodem.c \
 	drivers/stemodem/voicecall.c drivers/stemodem/radio-settings.c \
 	drivers/stemodem/gprs-context.c drivers/stemodem/caif_socket.h \
@@ -374,6 +379,7 @@
 @ATMODEM_TRUE@	drivers/ifxmodem/voicecall.$(OBJEXT) \
 @ATMODEM_TRUE@	drivers/ifxmodem/audio-settings.$(OBJEXT) \
 @ATMODEM_TRUE@	drivers/ifxmodem/radio-settings.$(OBJEXT) \
+ at ATMODEM_TRUE@	drivers/ifxmodem/gprs-context.$(OBJEXT) \
 @ATMODEM_TRUE@	drivers/ifxmodem/stk.$(OBJEXT) \
 @ATMODEM_TRUE@	drivers/stemodem/stemodem.$(OBJEXT) \
 @ATMODEM_TRUE@	drivers/stemodem/voicecall.$(OBJEXT) \
@@ -520,7 +526,8 @@
 man8dir = $(mandir)/man8
 NROFF = nroff
 MANS = $(dist_man_MANS)
-DATA = $(conf_DATA) $(dbusconf_DATA) $(rules_DATA) $(state_DATA)
+DATA = $(conf_DATA) $(dbusconf_DATA) $(rules_DATA) $(state_DATA) \
+	$(systemdunit_DATA)
 HEADERS = $(include_HEADERS) $(nodist_include_HEADERS)
 ETAGS = etags
 CTAGS = ctags
@@ -553,6 +560,7 @@
 CYGPATH_W = @CYGPATH_W@
 DBUS_CFLAGS = @DBUS_CFLAGS@
 DBUS_CONFDIR = @DBUS_CONFDIR@
+DBUS_DATADIR = @DBUS_DATADIR@
 DBUS_LIBS = @DBUS_LIBS@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
@@ -604,6 +612,9 @@
 SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
 STRIP = @STRIP@
+SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@
+SYSTEMD_LIBS = @SYSTEMD_LIBS@
+SYSTEMD_UNITDIR = @SYSTEMD_UNITDIR@
 UDEV_CFLAGS = @UDEV_CFLAGS@
 UDEV_DATADIR = @UDEV_DATADIR@
 UDEV_LIBS = @UDEV_LIBS@
@@ -679,6 +690,8 @@
 
 @DATAFILES_TRUE at dbusconfdir = @DBUS_CONFDIR@
 @DATAFILES_TRUE at dbusconf_DATA = src/ofono.conf
+ at DATAFILES_TRUE@@SYSTEMD_TRUE at systemdunitdir = @SYSTEMD_UNITDIR@
+ at DATAFILES_TRUE@@SYSTEMD_TRUE at systemdunit_DATA = src/ofono.service
 @DATAFILES_TRUE at confdir = $(sysconfdir)/ofono
 @DATAFILES_TRUE at conf_DATA = plugins/modem.conf
 @DATAFILES_TRUE at statedir = $(localstatedir)/lib/ofono
@@ -714,6 +727,7 @@
 				gatchat/gatutil.h gatchat/gatutil.c \
 				gatchat/gat.h \
 				gatchat/gatserver.h gatchat/gatserver.c \
+				gatchat/gatrawip.h gatchat/gatrawip.c \
 				gatchat/gathdlc.c gatchat/gathdlc.h \
 				gatchat/gatppp.c gatchat/gatppp.h \
 				gatchat/ppp.h gatchat/ppp_cp.h \
@@ -784,6 +798,7 @@
 		test/enter-pin \
 		test/hangup-all \
 		test/hangup-active \
+		test/set-roaming-allowed \
 		test/set-context \
 		test/list-contexts \
 		test/list-modems \
@@ -822,7 +837,8 @@
 		test/lock-pin \
 		test/unlock-pin \
 		test/enable-gprs \
-		test/disable-gprs
+		test/disable-gprs \
+		test/get-icon
 
 @TEST_TRUE at testdir = $(pkglibdir)/test
 @TEST_TRUE at test_SCRIPTS = $(test_scripts)
@@ -930,6 +946,8 @@
 	-rm -f config.h stamp-h1
 include/version.h: $(top_builddir)/config.status $(top_srcdir)/include/version.h.in
 	cd $(top_builddir) && $(SHELL) ./config.status $@
+src/ofono.service: $(top_builddir)/config.status $(top_srcdir)/src/ofono.service.in
+	cd $(top_builddir) && $(SHELL) ./config.status $@
 
 clean-noinstPROGRAMS:
 	@list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
@@ -1012,6 +1030,8 @@
 	gatchat/$(DEPDIR)/$(am__dirstamp)
 gatchat/gatserver.$(OBJEXT): gatchat/$(am__dirstamp) \
 	gatchat/$(DEPDIR)/$(am__dirstamp)
+gatchat/gatrawip.$(OBJEXT): gatchat/$(am__dirstamp) \
+	gatchat/$(DEPDIR)/$(am__dirstamp)
 gatchat/gathdlc.$(OBJEXT): gatchat/$(am__dirstamp) \
 	gatchat/$(DEPDIR)/$(am__dirstamp)
 gatchat/gatppp.$(OBJEXT): gatchat/$(am__dirstamp) \
@@ -1307,6 +1327,9 @@
 drivers/ifxmodem/radio-settings.$(OBJEXT):  \
 	drivers/ifxmodem/$(am__dirstamp) \
 	drivers/ifxmodem/$(DEPDIR)/$(am__dirstamp)
+drivers/ifxmodem/gprs-context.$(OBJEXT):  \
+	drivers/ifxmodem/$(am__dirstamp) \
+	drivers/ifxmodem/$(DEPDIR)/$(am__dirstamp)
 drivers/ifxmodem/stk.$(OBJEXT): drivers/ifxmodem/$(am__dirstamp) \
 	drivers/ifxmodem/$(DEPDIR)/$(am__dirstamp)
 drivers/stemodem/$(am__dirstamp):
@@ -1567,6 +1590,7 @@
 	-rm -f drivers/huaweimodem/huaweimodem.$(OBJEXT)
 	-rm -f drivers/huaweimodem/voicecall.$(OBJEXT)
 	-rm -f drivers/ifxmodem/audio-settings.$(OBJEXT)
+	-rm -f drivers/ifxmodem/gprs-context.$(OBJEXT)
 	-rm -f drivers/ifxmodem/ifxmodem.$(OBJEXT)
 	-rm -f drivers/ifxmodem/radio-settings.$(OBJEXT)
 	-rm -f drivers/ifxmodem/stk.$(OBJEXT)
@@ -1608,6 +1632,7 @@
 	-rm -f gatchat/gatio.$(OBJEXT)
 	-rm -f gatchat/gatmux.$(OBJEXT)
 	-rm -f gatchat/gatppp.$(OBJEXT)
+	-rm -f gatchat/gatrawip.$(OBJEXT)
 	-rm -f gatchat/gatresult.$(OBJEXT)
 	-rm -f gatchat/gatserver.$(OBJEXT)
 	-rm -f gatchat/gatsyntax.$(OBJEXT)
@@ -1743,6 +1768,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote at drivers/huaweimodem/$(DEPDIR)/huaweimodem.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at drivers/huaweimodem/$(DEPDIR)/voicecall.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at drivers/ifxmodem/$(DEPDIR)/audio-settings.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at drivers/ifxmodem/$(DEPDIR)/gprs-context.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at drivers/ifxmodem/$(DEPDIR)/ifxmodem.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at drivers/ifxmodem/$(DEPDIR)/radio-settings.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at drivers/ifxmodem/$(DEPDIR)/stk.Po at am__quote@
@@ -1784,6 +1810,7 @@
 @AMDEP_TRUE@@am__include@ @am__quote at gatchat/$(DEPDIR)/gatio.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at gatchat/$(DEPDIR)/gatmux.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at gatchat/$(DEPDIR)/gatppp.Po at am__quote@
+ at AMDEP_TRUE@@am__include@ @am__quote at gatchat/$(DEPDIR)/gatrawip.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at gatchat/$(DEPDIR)/gatresult.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at gatchat/$(DEPDIR)/gatserver.Po at am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote at gatchat/$(DEPDIR)/gatsyntax.Po at am__quote@
@@ -2038,6 +2065,26 @@
 	test -n "$$files" || exit 0; \
 	echo " ( cd '$(DESTDIR)$(statedir)' && rm -f" $$files ")"; \
 	cd "$(DESTDIR)$(statedir)" && rm -f $$files
+install-systemdunitDATA: $(systemdunit_DATA)
+	@$(NORMAL_INSTALL)
+	test -z "$(systemdunitdir)" || $(MKDIR_P) "$(DESTDIR)$(systemdunitdir)"
+	@list='$(systemdunit_DATA)'; test -n "$(systemdunitdir)" || list=; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  echo "$$d$$p"; \
+	done | $(am__base_list) | \
+	while read files; do \
+	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(systemdunitdir)'"; \
+	  $(INSTALL_DATA) $$files "$(DESTDIR)$(systemdunitdir)" || exit $$?; \
+	done
+
+uninstall-systemdunitDATA:
+	@$(NORMAL_UNINSTALL)
+	@list='$(systemdunit_DATA)'; test -n "$(systemdunitdir)" || list=; \
+	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+	test -n "$$files" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(systemdunitdir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(systemdunitdir)" && rm -f $$files
 install-includeHEADERS: $(include_HEADERS)
 	@$(NORMAL_INSTALL)
 	test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@@ -2296,7 +2343,7 @@
 all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(MANS) $(DATA) $(HEADERS) \
 		config.h
 installdirs:
-	for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(testdir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(confdir)" "$(DESTDIR)$(dbusconfdir)" "$(DESTDIR)$(rulesdir)" "$(DESTDIR)$(statedir)" "$(DESTDIR)$(includedir)" "$(DESTDIR)$(includedir)"; do \
+	for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(testdir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(confdir)" "$(DESTDIR)$(dbusconfdir)" "$(DESTDIR)$(rulesdir)" "$(DESTDIR)$(statedir)" "$(DESTDIR)$(systemdunitdir)" "$(DESTDIR)$(includedir)" "$(DESTDIR)$(includedir)"; do \
 	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
 	done
 install: install-am
@@ -2389,7 +2436,7 @@
 install-data-am: install-confDATA install-dbusconfDATA \
 	install-includeHEADERS install-man \
 	install-nodist_includeHEADERS install-rulesDATA \
-	install-stateDATA install-testSCRIPTS
+	install-stateDATA install-systemdunitDATA install-testSCRIPTS
 
 install-dvi: install-dvi-am
 
@@ -2441,7 +2488,7 @@
 	uninstall-includeHEADERS uninstall-man \
 	uninstall-nodist_includeHEADERS uninstall-rulesDATA \
 	uninstall-sbinPROGRAMS uninstall-stateDATA \
-	uninstall-testSCRIPTS
+	uninstall-systemdunitDATA uninstall-testSCRIPTS
 
 uninstall-man: uninstall-man8
 
@@ -2462,15 +2509,15 @@
 	install-nodist_includeHEADERS install-pdf install-pdf-am \
 	install-ps install-ps-am install-rulesDATA \
 	install-sbinPROGRAMS install-stateDATA install-strip \
-	install-testSCRIPTS installcheck installcheck-am installdirs \
-	maintainer-clean maintainer-clean-generic mostlyclean \
-	mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
-	pdf pdf-am ps ps-am tags uninstall uninstall-am \
-	uninstall-confDATA uninstall-dbusconfDATA \
-	uninstall-includeHEADERS uninstall-man uninstall-man8 \
-	uninstall-nodist_includeHEADERS uninstall-rulesDATA \
-	uninstall-sbinPROGRAMS uninstall-stateDATA \
-	uninstall-testSCRIPTS
+	install-systemdunitDATA install-testSCRIPTS installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags uninstall uninstall-am uninstall-confDATA \
+	uninstall-dbusconfDATA uninstall-includeHEADERS uninstall-man \
+	uninstall-man8 uninstall-nodist_includeHEADERS \
+	uninstall-rulesDATA uninstall-sbinPROGRAMS uninstall-stateDATA \
+	uninstall-systemdunitDATA uninstall-testSCRIPTS
 
 
 src/plugin.$(OBJEXT): src/builtin.h
--- TODO
+++ TODO
@@ -80,22 +80,42 @@
   Complexity: C1
   Owner: Kristen Carlson Accardi <kristen at linux.intel.com>
 
+- Add CDMA support to the SMS stack. The idea is to support only the PDU
+  mode. To start with only Submit and Deliver message handling for WMT
+  teleservice will be added to bring the basic CDMA SMS send and receive
+  functionality.
 
-SIM / SIM File system
-=====================
+  Priority: Low
+  Complexity: C8
+  Owner: Rajesh Kadhiravan Nagaiah <Rajesh.Nagaiah at elektrobit.com>
 
-- Fixed Dialing support.  oFono should support Fixed Dialing Numbers.  This
-  requires being able to edit the FD phonebook or assume that the FD phonebook
-  has been appropriately bootstrapped.
+- Add CDMA Delivery(Status) Report handling to the SMS stack.
 
   Priority: Low
-  Complexity: C2
+  Complexity: C4
+  Owner: Rajesh Kadhiravan Nagaiah <Rajesh.Nagaiah at elektrobit.com>
 
-- Barred Numbers.  oFono should support Barred Numbers capability.  This
-  requires ability to read & write EFadn entries on the SIM.
+- Add CDMA Voice Mail Notification handling to the SMS stack. In CDMA the
+  Message Waiting indication is notified through a specific teleservice ID
+  VMN. No update to corresponding elementary files required since they are
+  not present in the R-UIM. This will result in the message waiting
+  indication being initially processed within the SMS atom and then being
+  passed for delivery to the message waiting atom. Furthemore note that in
+  CDMA only voice mail type is supported.
 
   Priority: Low
   Complexity: C4
+  Owner: Rajesh Kadhiravan Nagaiah <Rajesh.Nagaiah at elektrobit.com>
+
+SIM / SIM File system
+=====================
+
+- Barred Dialing numbers support.  BDN will not be supported by oFono.
+  If BDN service enabled SIM is used, oFono will go into emergency mode.
+
+  Priority: Low
+  Complexity: C2
+  Owner: Jeevaka Badrappan <jeevaka.badrappan at elektrobit.com>
 
 - Read / Write EFcfis.  Call forwarding settings can be bootstrapped on the
   SIM for faster notification of the user that call forwarding is active.
@@ -135,6 +155,7 @@
 
   Priority: High
   Complexity: C2
+  Owner: Pekka Pessi <pekka.pessi at nokia.com>
 
 
 Modem Emulator
@@ -199,7 +220,12 @@
 
   Priority: High
   Complexity: C2
-  Owner: Zhenhua Zhang <zhenhua.zhang at intel.com>
+
+- IPv6 CP support.  To support IPv6 based GPRS contexts via PPP, GAtPPP
+  needs to be updated to support IPv6CP from RFC 2472.
+
+  Priority: Low
+  Complexity: C4
 
 - On-demand SOCKS5 proxy support for MMS contexts.  When an MMS context type
   is created, oFono should create a SOCKS5 proxy.  When a client connects to
@@ -282,6 +308,12 @@
   Complexity: C1
   Owner: Pekka Pessi <pekka.pessi at nokia.com>
 
+- Long phone numbers. According to 3GPP TS 24.008, the callee address
+  can be up to 80 digits long.
+
+  Priority: Medium
+  Complexity: C2
+
 - Dial strings. Include CLIR prefixes and 2nd stage dial strings in the
   DialString call property. Add dialstring accessor method to C API.
 
@@ -371,13 +403,6 @@
   Priority: High
   Complexity: C8
 
-- Support Send DTMF proactive command.  The DTMF characters are passed
-  directly to the voicecall atom and the SimToolkitAgent is notified if the
-  USIM has indicated it is acceptable to inform the user.
-
-  Priority: High
-  Complexity: C4
-
 - Support 'SMS-PP' Download Envelope.  Whenever a special type of SMS is
   received indicating that this is an SMS-PP Download message, this message
   will be sent to the SIM via an SMS-PP Download Envelope.  No user interaction
@@ -387,35 +412,12 @@
   Priority: High
   Complexity: C2
 
-- Support SIM icon files as defined in 3GPP 31.102.  The icons should be
-  automatically read from the SIM, converted to a usable format (such as XPM)
-  and cached inside the daemon.  This will require to read the contents of
-  EFimg and subsequently request the icon files.
-
-  Priority: High
-  Complexity: C8
-  Owner: Kristen Carlson Accardi <kristen at linux.intel.com>
-
-- Provide access to SIM icons for UI applications.  This should be exposed via
-  the GetIcon method on the SimToolkit interface.
-
-  Priority: High
-  Complexity: C2
-  Owner: Kristen Carlson Accardi <kristen at linux.intel.com>
-
 - Support Language Notification proactive command.
 
   Priority: Medium
   Complexity: C1
   Owner: Jeevaka Badrappan <jeevaka.badrappan at elektrobit.com>
 
-- Send HTML formatted text elements for all proactive commands, if the
-  proactive command supplies Text Attribute objects.  The stk_text_to_html
-  utility function can be used to convert raw text into HTML.
-
-  Priority: Low
-  Complexity: C2
-
 
 Emergency Calls
 ===============
--- configure
+++ configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.63 for ofono 0.31.
+# Generated by GNU Autoconf 2.63 for ofono 0.33.
 #
 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
 # 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
@@ -743,8 +743,8 @@
 # Identity of this package.
 PACKAGE_NAME='ofono'
 PACKAGE_TARNAME='ofono'
-PACKAGE_VERSION='0.31'
-PACKAGE_STRING='ofono 0.31'
+PACKAGE_VERSION='0.33'
+PACKAGE_STRING='ofono 0.33'
 PACKAGE_BUGREPORT=''
 
 ac_default_prefix=/usr/local
@@ -801,6 +801,12 @@
 UDEV_CFLAGS
 CAPNG_LIBS
 CAPNG_CFLAGS
+SYSTEMD_FALSE
+SYSTEMD_TRUE
+SYSTEMD_UNITDIR
+SYSTEMD_LIBS
+SYSTEMD_CFLAGS
+DBUS_DATADIR
 DBUS_CONFDIR
 DBUS_LIBS
 DBUS_CFLAGS
@@ -938,6 +944,8 @@
 enable_pie
 enable_threads
 with_dbusconfdir
+with_dbusdatadir
+enable_systemd
 enable_capng
 enable_udev
 enable_isimodem
@@ -960,6 +968,8 @@
 GTHREAD_LIBS
 DBUS_CFLAGS
 DBUS_LIBS
+SYSTEMD_CFLAGS
+SYSTEMD_LIBS
 CAPNG_CFLAGS
 CAPNG_LIBS
 UDEV_CFLAGS
@@ -1516,7 +1526,7 @@
   # 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 0.31 to adapt to many kinds of systems.
+\`configure' configures ofono 0.33 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1586,7 +1596,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of ofono 0.31:";;
+     short | recursive ) echo "Configuration of ofono 0.33:";;
    esac
   cat <<\_ACEOF
 
@@ -1610,6 +1620,7 @@
   --enable-test           enable test/example scripts
   --enable-pie            enable position independent executables flag
   --enable-threads        enable threading support
+  --enable-systemd        enable systemd support
   --enable-capng          enable capabilities support
   --disable-udev          don't use udev support even if available
   --disable-isimodem      disable PhoNet/ISI modem support
@@ -1623,6 +1634,7 @@
                           both]
   --with-gnu-ld           assume the C compiler uses GNU ld [default=no]
   --with-dbusconfdir=PATH path to D-Bus config directory
+  --with-dbusdatadir=PATH path to D-Bus data directory
 
 Some influential environment variables:
   PKG_CONFIG  path to pkg-config utility
@@ -1642,6 +1654,10 @@
               linker flags for GTHREAD, overriding pkg-config
   DBUS_CFLAGS C compiler flags for DBUS, overriding pkg-config
   DBUS_LIBS   linker flags for DBUS, overriding pkg-config
+  SYSTEMD_CFLAGS
+              C compiler flags for SYSTEMD, overriding pkg-config
+  SYSTEMD_LIBS
+              linker flags for SYSTEMD, overriding pkg-config
   CAPNG_CFLAGS
               C compiler flags for CAPNG, overriding pkg-config
   CAPNG_LIBS  linker flags for CAPNG, overriding pkg-config
@@ -1713,7 +1729,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-ofono configure 0.31
+ofono configure 0.33
 generated by GNU Autoconf 2.63
 
 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1727,7 +1743,7 @@
 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 0.31, which was
+It was created by ofono $as_me 0.33, which was
 generated by GNU Autoconf 2.63.  Invocation command line was
 
   $ $0 $@
@@ -2577,7 +2593,7 @@
 
 # Define the identity of the package.
  PACKAGE='ofono'
- VERSION='0.31'
+ VERSION='0.33'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -5795,13 +5811,13 @@
 else
   lt_cv_nm_interface="BSD nm"
   echo "int some_variable = 0;" > conftest.$ac_ext
-  (eval echo "\"\$as_me:5798: $ac_compile\"" >&5)
+  (eval echo "\"\$as_me:5814: $ac_compile\"" >&5)
   (eval "$ac_compile" 2>conftest.err)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:5801: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+  (eval echo "\"\$as_me:5817: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
   (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
   cat conftest.err >&5
-  (eval echo "\"\$as_me:5804: output\"" >&5)
+  (eval echo "\"\$as_me:5820: output\"" >&5)
   cat conftest.out >&5
   if $GREP 'External.*some_variable' conftest.out > /dev/null; then
     lt_cv_nm_interface="MS dumpbin"
@@ -7006,7 +7022,7 @@
   ;;
 *-*-irix6*)
   # Find out which ABI we are using.
-  echo '#line 7009 "configure"' > conftest.$ac_ext
+  echo '#line 7025 "configure"' > conftest.$ac_ext
   if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
   (eval $ac_compile) 2>&5
   ac_status=$?
@@ -8829,11 +8845,11 @@
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:8832: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:8848: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:8836: \$? = $ac_status" >&5
+   echo "$as_me:8852: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -9168,11 +9184,11 @@
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:9171: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:9187: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:9175: \$? = $ac_status" >&5
+   echo "$as_me:9191: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -9273,11 +9289,11 @@
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:9276: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:9292: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:9280: \$? = $ac_status" >&5
+   echo "$as_me:9296: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -9328,11 +9344,11 @@
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:9331: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:9347: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:9335: \$? = $ac_status" >&5
+   echo "$as_me:9351: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -12131,7 +12147,7 @@
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12134 "configure"
+#line 12150 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12227,7 +12243,7 @@
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<_LT_EOF
-#line 12230 "configure"
+#line 12246 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -13050,6 +13066,113 @@
 fi
 
 
+
+# Check whether --with-dbusdatadir was given.
+if test "${with_dbusdatadir+set}" = set; then
+  withval=$with_dbusdatadir; path_dbusdata=${withval}
+else
+  path_dbusdata="`$PKG_CONFIG --variable=datadir dbus-1`"
+fi
+
+if (test -z "${path_dbusdata}"); then
+	DBUS_DATADIR="${datadir}/dbus-1/system-services"
+else
+	DBUS_DATADIR="${path_dbusdata}/dbus-1/system-services"
+fi
+
+
+# Check whether --enable-systemd was given.
+if test "${enable_systemd+set}" = set; then
+  enableval=$enable_systemd; enable_systemd=${enableval}
+fi
+
+if (test "${enable_systemd}" = "yes"); then
+
+pkg_failed=no
+{ $as_echo "$as_me:$LINENO: checking for SYSTEMD" >&5
+$as_echo_n "checking for SYSTEMD... " >&6; }
+
+if test -n "$SYSTEMD_CFLAGS"; then
+    pkg_cv_SYSTEMD_CFLAGS="$SYSTEMD_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"systemd\"") >&5
+  ($PKG_CONFIG --exists --print-errors "systemd") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_SYSTEMD_CFLAGS=`$PKG_CONFIG --cflags "systemd" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$SYSTEMD_LIBS"; then
+    pkg_cv_SYSTEMD_LIBS="$SYSTEMD_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"systemd\"") >&5
+  ($PKG_CONFIG --exists --print-errors "systemd") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_SYSTEMD_LIBS=`$PKG_CONFIG --libs "systemd" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+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
+	        SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "systemd" 2>&1`
+        else
+	        SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --print-errors "systemd" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$SYSTEMD_PKG_ERRORS" >&5
+
+	{ $as_echo "$as_me:$LINENO: result: no" >&5
+$as_echo "no" >&6; }
+                { { $as_echo "$as_me:$LINENO: error: systemd is required" >&5
+$as_echo "$as_me: error: systemd is required" >&2;}
+   { (exit 1); exit 1; }; }
+elif test $pkg_failed = untried; then
+	{ { $as_echo "$as_me:$LINENO: error: systemd is required" >&5
+$as_echo "$as_me: error: systemd is required" >&2;}
+   { (exit 1); exit 1; }; }
+else
+	SYSTEMD_CFLAGS=$pkg_cv_SYSTEMD_CFLAGS
+	SYSTEMD_LIBS=$pkg_cv_SYSTEMD_LIBS
+        { $as_echo "$as_me:$LINENO: result: yes" >&5
+$as_echo "yes" >&6; }
+	dummy=yes
+fi
+	SYSTEMD_UNITDIR="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`"
+	if (test -z "${SYSTEM_UNITDIR}"); then
+		SYSTEMD_UNITDIR="/lib/systemd/system"
+	fi
+
+fi
+ if test "${enable_systemd}" = "yes"; then
+  SYSTEMD_TRUE=
+  SYSTEMD_FALSE='#'
+else
+  SYSTEMD_TRUE='#'
+  SYSTEMD_FALSE=
+fi
+
+
 # Check whether --enable-capng was given.
 if test "${enable_capng+set}" = set; then
   enableval=$enable_capng; enable_capng=${enableval}
@@ -13290,7 +13413,6 @@
 	storagedir="${localstatedir}/lib/ofono"
 fi
 
-
 cat >>confdefs.h <<_ACEOF
 #define STORAGEDIR "${storagedir}"
 _ACEOF
@@ -13302,13 +13424,12 @@
 	configdir="${sysconfdir}/ofono"
 fi
 
-
 cat >>confdefs.h <<_ACEOF
 #define CONFIGDIR "${configdir}"
 _ACEOF
 
 
-ac_config_files="$ac_config_files Makefile include/version.h"
+ac_config_files="$ac_config_files Makefile include/version.h src/ofono.service"
 
 cat >confcache <<\_ACEOF
 # This file is a shell script that caches the results of configure
@@ -13450,6 +13571,13 @@
 Usually this means the macro was only invoked conditionally." >&2;}
    { (exit 1); exit 1; }; }
 fi
+if test -z "${SYSTEMD_TRUE}" && test -z "${SYSTEMD_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"SYSTEMD\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"SYSTEMD\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
 if test -z "${UDEV_TRUE}" && test -z "${UDEV_FALSE}"; then
   { { $as_echo "$as_me:$LINENO: error: conditional \"UDEV\" was never defined.
 Usually this means the macro was only invoked conditionally." >&5
@@ -13800,7 +13928,7 @@
 # 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 0.31, which was
+This file was extended by ofono $as_me 0.33, which was
 generated by GNU Autoconf 2.63.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -13863,7 +13991,7 @@
 _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_version="\\
-ofono config.status 0.31
+ofono config.status 0.33
 configured by $0, generated by GNU Autoconf 2.63,
   with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
 
@@ -14246,6 +14374,7 @@
     "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
     "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
     "include/version.h") CONFIG_FILES="$CONFIG_FILES include/version.h" ;;
+    "src/ofono.service") CONFIG_FILES="$CONFIG_FILES src/ofono.service" ;;
 
   *) { { $as_echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
 $as_echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
--- configure.ac
+++ configure.ac
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(ofono, 0.31)
+AC_INIT(ofono, 0.33)
 
 AM_INIT_AUTOMAKE([foreign subdir-objects])
 AM_CONFIG_HEADER(config.h)
@@ -102,6 +102,29 @@
 fi
 AC_SUBST(DBUS_CONFDIR)
 
+AC_ARG_WITH(dbusdatadir, AC_HELP_STRING([--with-dbusdatadir=PATH],
+	[path to D-Bus data directory]), [path_dbusdata=${withval}],
+		[path_dbusdata="`$PKG_CONFIG --variable=datadir dbus-1`"])
+if (test -z "${path_dbusdata}"); then
+	DBUS_DATADIR="${datadir}/dbus-1/system-services"
+else
+	DBUS_DATADIR="${path_dbusdata}/dbus-1/system-services"
+fi
+AC_SUBST(DBUS_DATADIR)
+
+AC_ARG_ENABLE(systemd, AC_HELP_STRING([--enable-systemd],
+		[enable systemd support]), [enable_systemd=${enableval}])
+if (test "${enable_systemd}" = "yes"); then
+	PKG_CHECK_MODULES(SYSTEMD, systemd, dummy=yes,
+				AC_MSG_ERROR(systemd is required))
+	SYSTEMD_UNITDIR="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`"
+	if (test -z "${SYSTEM_UNITDIR}"); then
+		SYSTEMD_UNITDIR="/lib/systemd/system"
+	fi
+	AC_SUBST(SYSTEMD_UNITDIR)
+fi
+AM_CONDITIONAL(SYSTEMD, test "${enable_systemd}" = "yes")
+
 AC_ARG_ENABLE(capng, AC_HELP_STRING([--enable-capng],
 		[enable capabilities support]), [enable_capng=${enableval}])
 if (test "${enable_capng}" = "yes"); then
@@ -160,7 +183,6 @@
 else
 	storagedir="${localstatedir}/lib/ofono"
 fi
-
 AC_DEFINE_UNQUOTED(STORAGEDIR, "${storagedir}",
 			[Directory for the storage files])
 
@@ -169,8 +191,7 @@
 else
 	configdir="${sysconfdir}/ofono"
 fi
-
 AC_DEFINE_UNQUOTED(CONFIGDIR, "${configdir}",
 			[Directory for the configuration files])
 
-AC_OUTPUT(Makefile include/version.h)
+AC_OUTPUT(Makefile include/version.h src/ofono.service)
--- doc/features.txt
+++ doc/features.txt
@@ -71,6 +71,23 @@
   SimToolkit interface.  Indications that this property has changed are
   handled by the usual means.
 
+- Send DTMF proactive command.  Whenever oFono receives the Send DTMF command,
+  it checks that there are calls in progress and DTMF is possible.  If so,
+  DTMF characters are passed to the voicecall atom to be transmitted to the
+  modem.  The appropriate terminal response is sent to the SIM once the DTMF
+  tones have been played or the call has been disconnected.
+
+- Sim icon support.  oFono supports icons that are stored on the SIM.  If the
+  SIM notifies oFono that an icon is available for a particular proactive
+  command, oFono passes this information to the UI.  The UI is able to obtain
+  the icons by using GetIcon method on the SimManager interface.  The icons
+  are read from the SIM and converted into XPM format.
+
+- Text attribute support.  Whenever oFono detects that text attributes have
+  been given to any text or alpha string, it applies them and converts the
+  resulting text to HTML.  The UI is expected to be able to display such
+  HTML formatted text.
+
 Envelopes:
 
 - Timer Expiration envelope support.  Whenever a timer expires (as started by
@@ -110,3 +127,11 @@
 - GPRS suspension event support.  The packet data service may be temporarily
   suspended while a circuit switched service such as voice call or SMS is
   active.
+
+SIM
+===
+
+- Fixed Dialing support.  oFono reads the necessary bits from the SIM to
+  check if FDN support is allocated and enabled in the SIM.  If enabled,
+  oFono halts the SIM initialization procedure and the modem remains in the
+  PRESIM state.  In this state oFono will only allow emergency calls.
--- doc/radio-settings-api.txt
+++ doc/radio-settings-api.txt
@@ -41,3 +41,25 @@
 				"gsm"	Only GSM used for radio access.
 				"umts"	Only UMTS used for radio access.
 				"lte"	Only LTE used for radio acccess.
+
+		boolean	FastDormancy [read-write, optional]
+
+			This property will enable or disable fast
+			dormancy. Fast dormancy refers to UE initiated
+			release of radio resources quickly after a
+			burst of data transfer has ended. Normally,
+			radio resources are released by the network
+			after a timeout configured by the network
+			operator. Fast dormancy allows the modem to
+			release radio resources more quickly.
+			Typically, fast dormancy would be enabled if
+			no data transfer is predicted to occur in the
+			near future, for instance, when the end user
+			is not actively using the device. This is a
+			major power-saving feature for mobile devices,
+			but can be ignored for USB sticks or PCI
+			devices.
+
+			If the modem does not support such a feature
+			the property should never be exposed to the
+			user.
--- doc/sim-api.txt
+++ doc/sim-api.txt
@@ -40,6 +40,12 @@
 			Deactivates the lock for the particular pin type.  The
 			current PIN is required for the operation to succeed.
 
+		array{byte} GetIcon(byte id)
+
+			Obtain the icon given by id.  Only ids greater than 1
+			are valid.  XPM format is currently used to return the
+			icon format.
+
 Signals		PropertyChanged(string name, variant value)
 
 			This signal indicates a changed value of the given
@@ -125,3 +131,10 @@
 
 			Contains the Intergrated Circuit Card Identifer (ICCID)
 			which is read directly from the SIM.
+
+		boolean FixedDialing [readonly]
+
+			True if Fixed Dialing service is enabled in SIM card.
+
+			If FDN is enabled, oFono halts the SIM initialization
+			procedure and only emergency calls are allowed.
--- doc/smartmessaging-api.txt
+++ doc/smartmessaging-api.txt
@@ -5,23 +5,22 @@
 Interface	org.ofono.SmartMessaging
 Object path	[variable prefix]/{modem0,modem1,...}
 
-Methods		object SendAppointment(array{bytes} appointment)
+Methods		object SendAppointment(string to, array{bytes} appointment)
 
-			Sends a vCalendar object via SMS.  The object is given
-			in the appointment argument.  It is not interpreted
+			Sends a vCalendar object in appointment to the number
+			in to.  The object in appointment is not interpreted
 			by oFono in any way.  If the object is too large to
-			fit into a single SMS, it is fragmented as
-			appropriate.  This method call returns the object
-			path of the queued SMS.
-
-		object SendBusinessCard(array{bytes} card)
-
-			Sends a vCard object via SMS.  The object is given
-			in the card argument.  It is not interpreted
-			by oFono in any way.  If the object is too large to
-			fit into a single SMS, it is fragmented as
-			appropriate.  This method call returns the object
-			path of the queued SMS.
+			fit into a single SMS, it is fragmented as appropriate.
+			This method call returns the object path of the queued
+			SMS.
+
+		object SendBusinessCard(string to, array{bytes} card)
+
+			Sends a vCard object in card to the number in to. The
+			object in card is not interpreted by oFono in any way.
+			If the object is too large to fit into a single SMS,
+			it is fragmented as appropriate.  This method call
+			returns the object path of the queued SMS.
 
 		void RegisterAgent(object path)
 
--- doc/stk-api.txt
+++ doc/stk-api.txt
@@ -55,27 +55,32 @@
 			Signal is emitted whenever a property has changed.
 			The new value is passed as the signal argument.
 
-Properties	string IdleText
+Properties	string IdleModeText [readonly]
 
 			Contains the text to be used when the home screen is
-			idle.  This text is set by the SIM and can be changed
+			idle.  This text is set by the SIM and can change
 			at any time.
 
-		array{struct{string, byte}} MainMenu
+		byte IdleModeIcon [readonly]
+
+			Contains the identifier of the icon accompanying
+			the idle mode text.
+
+		array{struct{string, byte}} MainMenu [readonly]
 
 			Contains the items that make up the main menu.  This
 			is populated by the SIM when it sends the Setup Menu
 			Proactive Command.  The main menu is always available,
-			but its contents can be changed at any time.
+			but its contents can be changed at any time.  Each
+			item contains the item label and icon identifier.
 
-		string MainMenuTitle
+		string MainMenuTitle [readonly]
 
 			Contains the title of the main menu.
 
-		array{byte} Icons
+		string MainMenuIcon [readonly]
 
-			Contains the identifiers of all available icons for
-			this SIM.
+			Contains the identifier of the icon for the main menu.
 
 SimToolkitAgent Hierarchy [experimental]
 ===============
@@ -203,6 +208,46 @@
 
 			Possible Errors: [service].Error.SimToolkit.EndSession
 
+		void PlayTone(string tone, string text, byte icon_id)
+
+			Tells the agent to play an audio tone once.  The
+			method should return once the tone is finished
+			playing.  The text parameter contains an optional
+			text to be displayed to the user.  The following
+			tones are defined:
+				"dial-tone"
+				"busy"
+				"congestion"
+				"radio-path-acknowledge"
+				"radio-path-not-available"
+				"error"
+				"call-waiting"
+				"ringing-tone"
+				"general-beep"
+				"positive-acknowledgement"
+				"negative-acknowledgement"
+				"user-ringing-tone"
+				"user-sms-alert"
+				"critical" (high priority)
+				"vibrate"
+				"happy"
+				"sad"
+				"urgent-action"
+				"question"
+				"message-received"
+
+			Possible Errors: [service].Error.SimToolkit.EndSession
+
+		void LoopTone(string tone, string text, byte icon_id)
+
+			Tells the agent to reproduce an audio tone in a
+			loop until the method call is cancelled.  See
+			PlayTone() above for the list of possible tone names.
+			The text parameter contains an optional text to
+			be displayed to the user.
+
+			Possible Errors: [service].Error.SimToolkit.EndSession
+
 		void Cancel()
 
 			Asks the agent to cancel any ongoing operation in
--- doc/supplementaryservices-api.txt
+++ doc/supplementaryservices-api.txt
@@ -7,15 +7,22 @@
 
 Methods		string, variant Initiate(string command)
 
-			Sends a USSD command string to the network
-			initiating a session.  When the request is handled
+			If the command is a recognized supplementary service
+			control string, the corresponding SS request is made
+			and the result is returned.
+
+			Otherwise the command is sent to the network
+			initiating a USSD session. When the request is handled
 			by the appropriate node of the network, the
 			method returns the response or an appropriate
-			error.  The network may be awaiting further response
+			error. The network may be awaiting further response
 			from the ME after returning from this method and no
 			new command can be initiated until this one is
 			cancelled or ended.
 
+			The output arguments are described in section
+			"Initiate method outptut arguments" below.
+
 			Possible Errors: [service].Error.Timedout
 			Possible Errors: [service].Error.Canceled
 
@@ -68,3 +75,93 @@
 			"user-response"		The network is waiting for the
 						user's response, client must
 						call Respond().
+
+
+Initiate method output arguments
+================================
+
+The first return argument and the corresponding second return argument are:
+
+	"USSD"				string ussd_response
+	"CallBarring"			(string ss_op, string cb_service,
+					dict cb_dict)
+	"CallForwarding"		(string ss_op, string cf_service,
+					dict cf_dict)
+	"CallWaiting"			(string ss_op, dict cw_dict)
+	"CallingLinePresentation"	(string ss_op, string status)
+	"CalledLinePresentation"	(string ss_op, string status)
+	"CallingLineRestriction"	(string ss_op, string clir_status)
+	"CalledLineRestriction"		(string ss_op, string status)
+
+ss_op contains the supplementary service operation:
+
+	"activation"
+	"registration"
+	"interrogation"
+	"deactivation"
+	"erasure"
+
+cb_service contains the call barring service for which the operation was
+requested:
+
+	"AllOutgoing"
+	"InternationalOutgoing"
+	"InternationalOutgoingExceptHome"
+	"AllIncoming"
+	"IncomingWhenRoaming"
+	"AllBarringServices"
+	"AllOutgoingServices"
+	"AllIncomingServices"
+
+cf_service contains the call forwarding service for which the operation was
+requested:
+
+	"Unconditional"
+	"Busy"
+	"NoReply"
+	"NotReachable"
+	"All"
+	"AllConditional"
+
+cb_dict contains basic service/call barring service combinations that were
+affected by SS operation and their current status ("enabled" or "disabled").
+The basic services are:
+
+	"Voice"
+	"Data"
+	"Fax"
+	"Sms"
+	"DataSync"
+	"DataAsync"
+	"DataPad"
+	"DataPacket"
+
+To those the name of call barring service is appended, so the property and
+value is for example:
+
+	"FaxIncomingWhenRoaming" : "disabled"
+
+cf_dict contains call forwarding properties affected by the operation.
+Propery names are formed from basic service name and call forwarding
+service name, for example:
+
+	"VoiceNoReply" : "+12345678"
+
+The property value is the phone number to which the call is forwarded.
+
+For "NoReply" service, there is also a timeout property, holding the timeout
+in seconds, for example:
+
+	"VoiceNoReplyTimeout" : 20
+
+cw_dict contains basic services with "CallWaiting" suffix that were affected
+by call waiting operation and their current status ("enabled" or "disabled"),
+for example:
+
+	"VoiceCallWaiting" : "enabled"
+
+status can be "disabled" or "enabled".
+clir_status can be "disabled", "permanent", "on" or "off".
+
+More information about supplementary services is provided in
+call-barring-api.txt, call-forwarding-api.txt and call-settings-api.txt
--- drivers/atmodem/gprs-context.c
+++ drivers/atmodem/gprs-context.c
@@ -27,6 +27,8 @@
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
 
 #include <glib.h>
 
@@ -40,6 +42,8 @@
 
 #include "atmodem.h"
 
+#define TUN_SYSFS_DIR "/sys/devices/virtual/misc/tun"
+
 #define STATIC_IP_NETMASK "255.255.255.255"
 
 static const char *none_prefix[] = { NULL };
@@ -255,8 +259,17 @@
 {
 	GAtChat *chat = data;
 	struct gprs_context_data *gcd;
+	struct stat st;
+
+	if (stat(TUN_SYSFS_DIR, &st) < 0) {
+		ofono_error("Missing support for TUN/TAP devices");
+		return -ENODEV;
+	}
+
+	gcd = g_try_new0(struct gprs_context_data, 1);
+	if (!gcd)
+		return -ENOMEM;
 
-	gcd = g_new0(struct gprs_context_data, 1);
 	gcd->chat = g_at_chat_clone(chat);
 
 	ofono_gprs_context_set_data(gc, gcd);
--- drivers/atmodem/network-registration.c
+++ drivers/atmodem/network-registration.c
@@ -56,6 +56,7 @@
 	int signal_min; /* min strength reported via CIND */
 	int signal_max; /* max strength reported via CIND */
 	int tech;
+	struct ofono_network_time time;
 	unsigned int vendor;
 };
 
@@ -417,7 +418,7 @@
 	g_at_result_iter_init(&iter, result);
 
 	while (g_at_result_iter_next(&iter, "+COPS:")) {
-		int status, tech;
+		int status, tech, plmn;
 		const char *l, *s, *n;
 		gboolean have_long = FALSE;
 
@@ -458,6 +459,9 @@
 
 			list[num].tech = tech;
 
+			if (!g_at_result_iter_next_number(&iter, &plmn))
+				plmn = 0;
+
 			if (!g_at_result_iter_close_list(&iter))
 				break;
 
@@ -676,6 +680,61 @@
 	ofono_netreg_strength_notify(netreg, strength);
 }
 
+static void ctzv_notify(GAtResult *result, gpointer user_data)
+{
+	struct ofono_netreg *netreg = user_data;
+	struct netreg_data *nd = ofono_netreg_get_data(netreg);
+	int year, mon, mday, hour, min, sec;
+	const char *tz, *time;
+	GAtResultIter iter;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+CTZV:"))
+		return;
+
+	if (!g_at_result_iter_next_unquoted_string(&iter, &tz))
+		return;
+
+	if (!g_at_result_iter_next_string(&iter, &time))
+		return;
+
+	DBG("tz %s time %s", tz, time);
+
+	if (sscanf(time, "%u/%u/%u,%u:%u:%u", &year, &mon, &mday,
+						&hour, &min, &sec) != 6)
+		return;
+
+	nd->time.sec = sec;
+	nd->time.min = min;
+	nd->time.hour = hour;
+	nd->time.mday = mday;
+	nd->time.mon = mon;
+	nd->time.year = 2000 + year;
+}
+
+static void ctzdst_notify(GAtResult *result, gpointer user_data)
+{
+	struct ofono_netreg *netreg = user_data;
+	struct netreg_data *nd = ofono_netreg_get_data(netreg);
+	int dst;
+	GAtResultIter iter;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+CTZDST:"))
+		return;
+
+	if (!g_at_result_iter_next_number(&iter, &dst))
+		return;
+
+	DBG("dst %d", dst);
+
+	nd->time.dst = dst;
+
+	ofono_netreg_time_notify(netreg, &nd->time);
+}
+
 static void cind_cb(gboolean ok, GAtResult *result, gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
@@ -1055,10 +1114,19 @@
 					FALSE, netreg, NULL);
 		break;
 	case OFONO_VENDOR_IFX:
-		g_at_chat_send(nd->chat, "AT+XMER=1", none_prefix,
-				NULL, NULL, NULL);
+		/* Register for specific signal strength reports */
 		g_at_chat_register(nd->chat, "+XCIEV:", ifx_xciev_notify,
-				FALSE, netreg, NULL);
+					FALSE, netreg, NULL);
+		g_at_chat_send(nd->chat, "AT+XMER=1", none_prefix,
+						NULL, NULL, NULL);
+
+		/* Register for network time update reports */
+		g_at_chat_register(nd->chat, "+CTZV:", ctzv_notify,
+					FALSE, netreg, NULL);
+		g_at_chat_register(nd->chat, "+CTZDST:", ctzdst_notify,
+					FALSE, netreg, NULL);
+		g_at_chat_send(nd->chat, "AT+CTZR=1", none_prefix,
+						NULL, NULL, NULL);
 		break;
 	case OFONO_VENDOR_ZTE:
 	case OFONO_VENDOR_NOKIA:
@@ -1132,6 +1200,14 @@
 	nd->chat = g_at_chat_clone(chat);
 	nd->vendor = vendor;
 	nd->tech = -1;
+	nd->time.sec = -1;
+	nd->time.min = -1;
+	nd->time.hour = -1;
+	nd->time.mday = -1;
+	nd->time.mon = -1;
+	nd->time.year = -1;
+	nd->time.dst = 0;
+	nd->time.utcoff = 0;
 	ofono_netreg_set_data(netreg, nd);
 
 	g_at_chat_send(nd->chat, "AT+CREG=?", creg_prefix,
--- drivers/atmodem/phonebook.c
+++ drivers/atmodem/phonebook.c
@@ -27,6 +27,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <errno.h>
 
 #include <glib.h>
 
@@ -39,6 +40,7 @@
 #include "gatresult.h"
 
 #include "atmodem.h"
+#include "vendor.h"
 
 #define INDEX_INVALID -1
 
@@ -57,6 +59,8 @@
 	char *old_charset;
 	int supported;
 	GAtChat *chat;
+	unsigned int vendor;
+	guint ready_id;
 };
 
 static void warn_bad()
@@ -208,7 +212,7 @@
 }
 
 static void at_read_entries_cb(gboolean ok, GAtResult *result,
-				gpointer user_data)
+						gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
 	struct ofono_phonebook *pb = cbd->user;
@@ -253,7 +257,7 @@
 }
 
 static void at_set_charset_cb(gboolean ok, GAtResult *result,
-				gpointer user_data)
+						gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
 
@@ -266,7 +270,7 @@
 }
 
 static void at_read_charset_cb(gboolean ok, GAtResult *result,
-					gpointer user_data)
+						gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
 	struct ofono_phonebook *pb = cbd->user;
@@ -304,7 +308,7 @@
 }
 
 static void at_list_indices_cb(gboolean ok, GAtResult *result,
-				gpointer user_data)
+						gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
 	struct ofono_phonebook *pb = cbd->user;
@@ -321,7 +325,7 @@
 	if (!g_at_result_iter_open_list(&iter))
 		goto error;
 
-	/* retrieve index_min and index_max from indices
+	/* Retrieve index_min and index_max from indices
 	 * which seems like "(1-150),32,16"
 	 */
 	if (!g_at_result_iter_next_range(&iter, &pbd->index_min,
@@ -340,7 +344,7 @@
 }
 
 static void at_select_storage_cb(gboolean ok, GAtResult *result,
-					gpointer user_data)
+						gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
 	struct ofono_phonebook *pb = cbd->user;
@@ -389,9 +393,28 @@
 }
 
 static void at_list_storages_cb(gboolean ok, GAtResult *result,
-					gpointer user_data)
+						gpointer user_data);
+
+static void ifx_pbready_notify(GAtResult *result, gpointer user_data)
 {
 	struct ofono_phonebook *pb = user_data;
+	struct pb_data *pbd = ofono_phonebook_get_data(pb);
+
+	g_at_chat_unregister(pbd->chat, pbd->ready_id);
+	pbd->ready_id = 0;
+
+	if (g_at_chat_send(pbd->chat, "AT+CPBS=?", cpbs_prefix,
+				at_list_storages_cb, pb, NULL) > 0)
+		return;
+
+	phonebook_not_supported(pb);
+}
+
+static void at_list_storages_cb(gboolean ok, GAtResult *result,
+						gpointer user_data)
+{
+	struct ofono_phonebook *pb = user_data;
+	struct pb_data *pbd = ofono_phonebook_get_data(pb);
 	gboolean sm_supported = FALSE;
 	gboolean me_supported = FALSE;
 	gboolean in_list = FALSE;
@@ -417,20 +440,28 @@
 	}
 
 	if (in_list && !g_at_result_iter_close_list(&iter))
-		goto error;
+		goto vendor;
 
 	if (!me_supported && !sm_supported)
-		goto error;
+		goto vendor;
 
 	ofono_phonebook_register(pb);
 	return;
 
+vendor:
+	switch (pbd->vendor) {
+	case OFONO_VENDOR_IFX:
+		pbd->ready_id = g_at_chat_register(pbd->chat, "+PBREADY",
+					ifx_pbready_notify, FALSE, pb, NULL);
+		return;
+	}
+
 error:
 	phonebook_not_supported(pb);
 }
 
 static void at_list_charsets_cb(gboolean ok, GAtResult *result,
-					gpointer user_data)
+						gpointer user_data)
 {
 	struct ofono_phonebook *pb = user_data;
 	struct pb_data *pbd = ofono_phonebook_get_data(pb);
@@ -504,8 +535,12 @@
 	GAtChat *chat = data;
 	struct pb_data *pbd;
 
-	pbd = g_new0(struct pb_data, 1);
+	pbd = g_try_new0(struct pb_data, 1);
+	if (!pbd)
+		return -ENOMEM;
+
 	pbd->chat = g_at_chat_clone(chat);
+	pbd->vendor = vendor;
 
 	ofono_phonebook_set_data(pb, pbd);
 
@@ -528,10 +563,10 @@
 }
 
 static struct ofono_phonebook_driver driver = {
-	.name			= "atmodem",
-	.probe			= at_phonebook_probe,
-	.remove			= at_phonebook_remove,
-	.export_entries		= at_export_entries
+	.name		= "atmodem",
+	.probe		= at_phonebook_probe,
+	.remove		= at_phonebook_remove,
+	.export_entries	= at_export_entries
 };
 
 void at_phonebook_init()
--- drivers/atmodem/sim.c
+++ drivers/atmodem/sim.c
@@ -41,11 +41,13 @@
 
 #include "atmodem.h"
 
+#define EF_STATUS_INVALIDATED 0
+#define EF_STATUS_VALID 1
+
 struct sim_data {
 	GAtChat *chat;
 	unsigned int vendor;
 	guint ready_id;
-	guint ready_source;
 };
 
 static const char *crsm_prefix[] = { "+CRSM:", NULL };
@@ -64,11 +66,12 @@
 	int flen, rlen;
 	int str;
 	unsigned char access[3];
+	unsigned char file_status;
 
 	decode_at_error(&error, g_at_result_final_response(result));
 
 	if (!ok) {
-		cb(&error, -1, -1, -1, NULL, cbd->data);
+		cb(&error, -1, -1, -1, NULL, EF_STATUS_INVALIDATED, cbd->data);
 		return;
 	}
 
@@ -88,27 +91,32 @@
 		error.type = OFONO_ERROR_TYPE_SIM;
 		error.error = (sw1 << 8) | sw2;
 
-		cb(&error, -1, -1, -1, NULL, cbd->data);
+		cb(&error, -1, -1, -1, NULL, EF_STATUS_INVALIDATED, cbd->data);
 		return;
 	}
 
 	DBG("crsm_info_cb: %02x, %02x, %i", sw1, sw2, len);
 
-	if (response[0] == 0x62)
+	if (response[0] == 0x62) {
 		ok = sim_parse_3g_get_response(response, len, &flen, &rlen,
 						&str, access, NULL);
+
+		file_status = EF_STATUS_VALID;
+	}
 	else
 		ok = sim_parse_2g_get_response(response, len, &flen, &rlen,
-						&str, access);
+						&str, access, &file_status);
 
 	if (!ok)
 		goto error;
 
-	cb(&error, flen, str, rlen, access, cbd->data);
+	cb(&error, flen, str, rlen, access, file_status, cbd->data);
+
 	return;
 
 error:
-	CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, cbd->data);
+	CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL,
+				EF_STATUS_INVALIDATED, cbd->data);
 }
 
 static void at_sim_read_info(struct ofono_sim *sim, int fileid,
@@ -123,7 +131,8 @@
 		unsigned char access[3] = { 0x00, 0x00, 0x00 };
 
 		if (fileid == SIM_EFAD_FILEID) {
-			CALLBACK_WITH_SUCCESS(cb, 4, 0, 0, access, data);
+			CALLBACK_WITH_SUCCESS(cb, 4, 0, 0, access,
+						EF_STATUS_VALID, data);
 			return;
 		}
 	}
@@ -142,7 +151,8 @@
 		return;
 
 error:
-	CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, data);
+	CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL,
+				EF_STATUS_INVALIDATED, data);
 }
 
 static void at_crsm_read_cb(gboolean ok, GAtResult *result,
@@ -522,18 +532,6 @@
 	CALLBACK_WITH_FAILURE(cb, -1, data);
 }
 
-static gboolean ready_notify_unregister(gpointer user_data)
-{
-	struct sim_data *sd = user_data;
-
-	sd->ready_source = 0;
-
-	g_at_chat_unregister(sd->chat, sd->ready_id);
-	sd->ready_id = 0;
-
-	return FALSE;
-}
-
 static void at_xsim_notify(GAtResult *result, gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
@@ -543,9 +541,6 @@
 	GAtResultIter iter;
 	int state;
 
-	if (sd->ready_source > 0)
-		return;
-
 	g_at_result_iter_init(&iter, result);
 
 	if (!g_at_result_iter_next(&iter, "+XSIM:"))
@@ -564,7 +559,8 @@
 
 	cb(&error, cbd->data);
 
-	sd->ready_source = g_timeout_add(0, ready_notify_unregister, sd);
+	g_at_chat_unregister(sd->chat, sd->ready_id);
+	sd->ready_id = 0;
 }
 
 static void at_epev_notify(GAtResult *result, gpointer user_data)
@@ -574,12 +570,10 @@
 	ofono_sim_lock_unlock_cb_t cb = cbd->cb;
 	struct ofono_error error = { .type = OFONO_ERROR_TYPE_NO_ERROR };
 
-	if (sd->ready_source > 0)
-		return;
-
 	cb(&error, cbd->data);
 
-	sd->ready_source = g_timeout_add(0, ready_notify_unregister, sd);
+	g_at_chat_unregister(sd->chat, sd->ready_id);
+	sd->ready_id = 0;
 }
 
 static void at_pin_send_cb(gboolean ok, GAtResult *result,
@@ -624,18 +618,6 @@
 	g_free(cbd);
 }
 
-static void at_lock_unlock_cb(gboolean ok, GAtResult *result,
-				gpointer user_data)
-{
-	struct cb_data *cbd = user_data;
-	ofono_sim_lock_unlock_cb_t cb = cbd->cb;
-	struct ofono_error error;
-
-	decode_at_error(&error, g_at_result_final_response(result));
-
-	cb(&error, cbd->data);
-}
-
 static void at_pin_send(struct ofono_sim *sim, const char *passwd,
 			ofono_sim_lock_unlock_cb_t cb, void *data)
 {
@@ -665,6 +647,38 @@
 	CALLBACK_WITH_FAILURE(cb, data);
 }
 
+static void at_pin_send_puk_cb(gboolean ok, GAtResult *result,
+				gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	struct sim_data *sd = cbd->user;
+	ofono_sim_lock_unlock_cb_t cb = cbd->cb;
+	struct ofono_error error;
+
+	decode_at_error(&error, g_at_result_final_response(result));
+
+	if (!ok)
+		goto done;
+
+	switch (sd->vendor) {
+	case OFONO_VENDOR_IFX:
+		/*
+		 * On the IFX modem, AT+CPIN? can return READY too
+		 * early and so use +XSIM notification to detect
+		 * the ready state of the SIM.
+		 */
+		sd->ready_id = g_at_chat_register(sd->chat, "+XSIM",
+							at_xsim_notify,
+							FALSE, cbd, g_free);
+		return;
+	}
+
+done:
+	cb(&error, cbd->data);
+
+	g_free(cbd);
+}
+
 static void at_pin_send_puk(struct ofono_sim *sim, const char *puk,
 				const char *passwd,
 				ofono_sim_lock_unlock_cb_t cb, void *data)
@@ -677,10 +691,12 @@
 	if (!cbd)
 		goto error;
 
+	cbd->user = sd;
+
 	snprintf(buf, sizeof(buf), "AT+CPIN=\"%s\",\"%s\"", puk, passwd);
 
 	ret = g_at_chat_send(sd->chat, buf, none_prefix,
-				at_lock_unlock_cb, cbd, g_free);
+				at_pin_send_puk_cb, cbd, NULL);
 
 	memset(buf, 0, sizeof(buf));
 
@@ -693,6 +709,18 @@
 	CALLBACK_WITH_FAILURE(cb, data);
 }
 
+static void at_lock_unlock_cb(gboolean ok, GAtResult *result,
+				gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	ofono_sim_lock_unlock_cb_t cb = cbd->cb;
+	struct ofono_error error;
+
+	decode_at_error(&error, g_at_result_final_response(result));
+
+	cb(&error, cbd->data);
+}
+
 static const char *const at_clck_cpwd_fac[] = {
 	[OFONO_SIM_PASSWORD_SIM_PIN] = "SC",
 	[OFONO_SIM_PASSWORD_SIM_PIN2] = "P2",
@@ -873,9 +901,6 @@
 
 	ofono_sim_set_data(sim, NULL);
 
-	if (sd->ready_source > 0)
-		g_source_remove(sd->ready_source);
-
 	g_at_chat_unref(sd->chat);
 	g_free(sd);
 }
--- drivers/atmodem/voicecall.c
+++ drivers/atmodem/voicecall.c
@@ -47,6 +47,9 @@
  /* Amount of time we give for CLIP to arrive before we commence CLCC poll */
 #define CLIP_INTERVAL 200
 
+ /* When +VTD returns 0, an unspecified manufacturer-specific delay is used */
+#define TONE_DURATION 1000
+
 static const char *clcc_prefix[] = { "+CLCC:", NULL };
 static const char *none_prefix[] = { NULL };
 
@@ -59,6 +62,9 @@
 	unsigned int clcc_source;
 	GAtChat *chat;
 	unsigned int vendor;
+	unsigned int tone_duration;
+	guint vts_source;
+	unsigned int vts_delay;
 };
 
 struct release_id_req {
@@ -522,14 +528,37 @@
 	at_template(buf, vc, generic_cb, incoming_or_waiting, cb, data);
 }
 
+static gboolean vts_timeout_cb(gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	struct voicecall_data *vd = cbd->user;
+	ofono_voicecall_cb_t cb = cbd->cb;
+
+	vd->vts_source = 0;
+
+	CALLBACK_WITH_SUCCESS(cb, cbd->data);
+	g_free(cbd);
+
+	return FALSE;
+}
+
 static void vts_cb(gboolean ok, GAtResult *result, gpointer user_data)
 {
 	struct cb_data *cbd = user_data;
+	struct voicecall_data *vd = cbd->user;
 	ofono_voicecall_cb_t cb = cbd->cb;
 	struct ofono_error error;
 
 	decode_at_error(&error, g_at_result_final_response(result));
-	cb(&error, cbd->data);
+
+	if (!ok) {
+		cb(&error, cbd->data);
+
+		g_free(cbd);
+		return;
+	}
+
+	vd->vts_source = g_timeout_add(vd->vts_delay, vts_timeout_cb, cbd);
 }
 
 static void at_send_dtmf(struct ofono_voicecall *vc, const char *dtmf,
@@ -545,19 +574,22 @@
 	if (!cbd)
 		goto error;
 
-	/* strlen("+VTS=\"T\";") = 9 + initial AT + null */
-	buf = g_try_new(char, len * 9 + 3);
+	cbd->user = vd;
 
+	/* strlen("+VTS=T;") = 7 + initial AT + null */
+	buf = g_try_new(char, len * 9 + 3);
 	if (!buf)
 		goto error;
 
-	s = sprintf(buf, "AT+VTS=\"%c\"", dtmf[0]);
+	s = sprintf(buf, "AT+VTS=%c", dtmf[0]);
 
 	for (i = 1; i < len; i++)
-		s += sprintf(buf + s, ";+VTS=\"%c\"", dtmf[i]);
+		s += sprintf(buf + s, ";+VTS=%c", dtmf[i]);
+
+	vd->vts_delay = vd->tone_duration * len;
 
 	s = g_at_chat_send(vd->chat, buf, none_prefix,
-				vts_cb, cbd, g_free);
+				vts_cb, cbd, NULL);
 
 	g_free(buf);
 
@@ -800,6 +832,26 @@
 			clcc_poll_cb, vc, NULL);
 }
 
+static void vtd_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_voicecall *vc = user_data;
+	struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+	GAtResultIter iter;
+	int duration;
+
+	if (!ok)
+		return;
+
+	g_at_result_iter_init(&iter, result);
+	g_at_result_iter_next(&iter, "+VTD:");
+
+	if (!g_at_result_iter_next_number(&iter, &duration))
+		return;
+
+	if (duration)
+		vd->tone_duration = duration * 100;
+}
+
 static void at_voicecall_initialized(gboolean ok, GAtResult *result,
 					gpointer user_data)
 {
@@ -840,12 +892,15 @@
 
 	vd->chat = g_at_chat_clone(chat);
 	vd->vendor = vendor;
+	vd->tone_duration = TONE_DURATION;
 
 	ofono_voicecall_set_data(vc, vd);
 
 	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+COLP=1", NULL, NULL, NULL, NULL);
+	g_at_chat_send(vd->chat, "AT+VTD?", NULL,
+				vtd_query_cb, vc, NULL);
 	g_at_chat_send(vd->chat, "AT+CCWA=1", NULL,
 				at_voicecall_initialized, vc, NULL);
 
@@ -859,6 +914,9 @@
 	if (vd->clcc_source)
 		g_source_remove(vd->clcc_source);
 
+	if (vd->vts_source)
+		g_source_remove(vd->vts_source);
+
 	g_slist_foreach(vd->calls, (GFunc) g_free, NULL);
 	g_slist_free(vd->calls);
 
--- drivers/huaweimodem/gprs-context.c
+++ drivers/huaweimodem/gprs-context.c
@@ -50,6 +50,7 @@
 	GAtChat *chat;
 	unsigned int active_context;
 	unsigned int dhcp_source;
+	unsigned int dhcp_count;
 	union {
 		ofono_gprs_context_cb_t down_cb;	/* Down callback */
 		ofono_gprs_context_up_cb_t up_cb;	/* Up callback */
@@ -64,8 +65,13 @@
 	struct ofono_gprs_context *gc = user_data;
 	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
 
-	check_dhcp(gc);
+	if (gcd->dhcp_count > 10)
+		CALLBACK_WITH_FAILURE(gcd->up_cb, NULL, 0, NULL, NULL,
+						NULL, NULL, gcd->cb_data);
+	else
+		check_dhcp(gc);
 
+	gcd->dhcp_count++;
 	gcd->dhcp_source = 0;
 
 	return FALSE;
@@ -103,6 +109,8 @@
 	char *dns1 = NULL;
 	char *dns2 = NULL;
 	const char *dns[3];
+	struct ofono_modem *modem;
+	const char *devnode;
 
 	DBG("");
 
@@ -132,6 +140,11 @@
 	ofono_info("IP: %s, Gateway: %s", ip, gateway);
 	ofono_info("DNS: %s, %s", dns1, dns2);
 
+	modem = ofono_gprs_context_get_modem(gc);
+	devnode = ofono_modem_get_string(modem, "NDIS");
+
+	ofono_info("NDIS: %s", devnode);
+
 	interface = "invalid";
 
 	CALLBACK_WITH_SUCCESS(gcd->up_cb, interface, TRUE, ip,
@@ -189,6 +202,8 @@
 		gcd->up_cb = cb;
 		gcd->cb_data = cbd->data;
 
+		gcd->dhcp_count = 0;
+
 		check_dhcp(gc);
 		return;
 	}
--- drivers/ifxmodem/gprs-context.c
+++ drivers/ifxmodem/gprs-context.c
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2010  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
+ *  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
+
+#define _GNU_SOURCE
+#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 "gatrawip.h"
+
+#include "ifxmodem.h"
+
+#define TUN_SYSFS_DIR "/sys/devices/virtual/misc/tun"
+
+#define STATIC_IP_NETMASK "255.255.255.255"
+
+static const char *none_prefix[] = { NULL };
+static const char *xdns_prefix[] = { "+XDNS:", NULL };
+static const char *cgpaddr_prefix[] = { "+CGPADDR:", NULL };
+
+enum state {
+	STATE_IDLE,
+	STATE_ENABLING,
+	STATE_DISABLING,
+	STATE_ACTIVE,
+};
+
+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];
+	GAtRawIP *rawip;
+	enum state state;
+	char address[32];
+	char dns1[32];
+	char dns2[32];
+	union {
+		ofono_gprs_context_cb_t down_cb;        /* Down callback */
+		ofono_gprs_context_up_cb_t up_cb;       /* Up callback */
+	};
+	void *cb_data;                                  /* Callback data */
+};
+
+static void rawip_debug(const char *str, void *data)
+{
+	ofono_info("%s: %s", (const char *) data, str);
+}
+
+static const char *setup_rawip(struct ofono_gprs_context *gc)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	GAtIO *io;
+
+	io = g_at_chat_get_io(gcd->chat);
+
+	g_at_chat_suspend(gcd->chat);
+
+	gcd->rawip = g_at_rawip_new_from_io(io);
+
+	if (gcd->rawip == NULL) {
+		g_at_chat_resume(gcd->chat);
+		return NULL;
+	}
+
+	if (getenv("OFONO_IP_DEBUG"))
+		g_at_rawip_set_debug(gcd->rawip, rawip_debug, "IP");
+
+	g_at_rawip_open(gcd->rawip);
+
+	return g_at_rawip_get_interface(gcd->rawip);
+}
+
+static void failed_setup(struct ofono_gprs_context *gc,
+				GAtResult *result, gboolean deactivate)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	struct ofono_error error;
+	char buf[64];
+
+	if (deactivate == TRUE) {
+		sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
+		g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL);
+	}
+
+	gcd->active_context = 0;
+	gcd->state = STATE_IDLE;
+
+	if (!result) {
+		CALLBACK_WITH_FAILURE(gcd->up_cb, NULL, 0, NULL, NULL,
+						NULL, NULL, gcd->cb_data);
+		return;
+	}
+
+	decode_at_error(&error, g_at_result_final_response(result));
+	gcd->up_cb(&error, NULL, 0, NULL, NULL, NULL, NULL, gcd->cb_data);
+}
+
+static void session_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);
+	const char *interface;
+	const char *dns[3];
+
+	if (!ok) {
+		ofono_error("Failed to establish session");
+		failed_setup(gc, result, TRUE);
+		return;
+	}
+
+	gcd->state = STATE_ACTIVE;
+
+	dns[0] = gcd->dns1;
+	dns[1] = gcd->dns2;
+	dns[2] = 0;
+
+	interface = setup_rawip(gc);
+	if (!interface)
+		interface = "invalid";
+
+	CALLBACK_WITH_SUCCESS(gcd->up_cb, interface, TRUE, gcd->address,
+				STATIC_IP_NETMASK, NULL, dns, gcd->cb_data);
+
+	gcd->up_cb = NULL;
+	gcd->cb_data = NULL;
+}
+
+static void dns_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);
+	char buf[64];
+	int cid;
+	const char *address;
+	GAtResultIter iter;
+
+	if (!ok) {
+		ofono_error("Unable to get DNS details");
+		failed_setup(gc, result, TRUE);
+		return;
+	}
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+XDNS:"))
+		goto error;
+
+	if (!g_at_result_iter_next_number(&iter, &cid))
+		goto error;
+
+	if ((unsigned int) cid != gcd->active_context)
+		goto error;
+
+	if (!g_at_result_iter_next_string(&iter, &address))
+		goto error;
+
+	strncpy(gcd->dns1, address, sizeof(gcd->dns1));
+
+	if (!g_at_result_iter_next_string(&iter, &address))
+		goto error;
+
+	strncpy(gcd->dns2, address, sizeof(gcd->dns2));
+
+	ofono_info("IP: %s", gcd->address);
+	ofono_info("DNS: %s, %s", gcd->dns1, gcd->dns2);
+
+	sprintf(buf, "AT+CGDATA=\"M-RAW_IP\",%d", gcd->active_context);
+	if (g_at_chat_send(gcd->chat, buf, none_prefix,
+					session_cb, gc, NULL) > 0)
+		return;
+
+error:
+	failed_setup(gc, NULL, TRUE);
+}
+
+static void address_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);
+	int cid;
+	const char *address;
+	GAtResultIter iter;
+
+	if (!ok) {
+		ofono_error("Unable to get context address");
+		failed_setup(gc, result, TRUE);
+		return;
+	}
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, "+CGPADDR:"))
+		goto error;
+
+	if (!g_at_result_iter_next_number(&iter, &cid))
+		goto error;
+
+	if ((unsigned int) cid != gcd->active_context)
+		goto error;
+
+	if (!g_at_result_iter_next_string(&iter, &address))
+		goto error;
+
+	strncpy(gcd->address, address, sizeof(gcd->address));
+
+	if (g_at_chat_send(gcd->chat, "AT+XDNS?", xdns_prefix,
+					dns_cb, gc, NULL) > 0)
+		return;
+
+error:
+	failed_setup(gc, NULL, TRUE);
+}
+
+static void activate_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);
+	char buf[64];
+
+	if (!ok) {
+		ofono_error("Unable to activate context");
+		failed_setup(gc, result, FALSE);
+		return;
+	}
+
+	sprintf(buf, "AT+CGPADDR=%u", gcd->active_context);
+	if (g_at_chat_send(gcd->chat, buf, cgpaddr_prefix,
+					address_cb, gc, NULL) > 0)
+		return;
+
+	failed_setup(gc, NULL, TRUE);
+}
+
+static void setup_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);
+	char buf[64];
+
+	if (!ok) {
+		ofono_error("Failed to setup context");
+		failed_setup(gc, result, FALSE);
+		return;
+	}
+
+	sprintf(buf, "AT+XDNS=%u,1", gcd->active_context);
+	g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL);
+
+	sprintf(buf, "AT+CGACT=1,%u", gcd->active_context);
+	if (g_at_chat_send(gcd->chat, buf, none_prefix,
+				activate_cb, gc, NULL) > 0)
+		return;
+
+	failed_setup(gc, NULL, FALSE);
+}
+
+static void ifx_gprs_activate_primary(struct ofono_gprs_context *gc,
+				const struct ofono_gprs_primary_context *ctx,
+				ofono_gprs_context_up_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;
+
+	gcd->active_context = ctx->cid;
+	gcd->up_cb = cb;
+	gcd->cb_data = data;
+	memcpy(gcd->username, ctx->username, sizeof(ctx->username));
+	memcpy(gcd->password, ctx->password, sizeof(ctx->password));
+
+	gcd->state = STATE_ENABLING;
+
+	len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid);
+
+	if (ctx->apn)
+		snprintf(buf + len, sizeof(buf) - len - 3,
+					",\"%s\"", ctx->apn);
+
+	if (g_at_chat_send(gcd->chat, buf, none_prefix,
+				setup_cb, gc, NULL) > 0)
+		return;
+
+	CALLBACK_WITH_FAILURE(cb, NULL, 0, NULL, NULL, NULL, NULL, 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);
+
+	g_at_rawip_unref(gcd->rawip);
+	gcd->rawip = NULL;
+
+	gcd->active_context = 0;
+	gcd->state = STATE_IDLE;
+
+	g_at_chat_resume(gcd->chat);
+
+	CALLBACK_WITH_SUCCESS(gcd->down_cb, gcd->cb_data);
+}
+
+static void ifx_gprs_deactivate_primary(struct ofono_gprs_context *gc,
+					unsigned int id,
+					ofono_gprs_context_cb_t cb, void *data)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+	GAtChat *chat = g_at_chat_get_slave(gcd->chat);
+	char buf[64];
+
+	DBG("");
+
+	gcd->state = STATE_DISABLING;
+	gcd->down_cb = cb;
+	gcd->cb_data = data;
+
+	g_at_rawip_shutdown(gcd->rawip);
+
+	sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
+	if (g_at_chat_send(chat, buf, none_prefix,
+				deactivate_cb, gc, NULL) > 0)
+		return;
+
+	CALLBACK_WITH_SUCCESS(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);
+	const char *event;
+	int cid;
+	GAtResultIter iter;
+
+	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 DEACT") == FALSE)
+		return;
+
+	if (!g_at_result_iter_skip_next(&iter))
+		return;
+
+	if (!g_at_result_iter_next_number(&iter, &cid))
+		return;
+
+	if ((unsigned int) cid != gcd->active_context)
+		return;
+
+	if (gcd->state != STATE_IDLE && gcd->rawip) {
+		g_at_rawip_unref(gcd->rawip);
+		gcd->rawip = NULL;
+
+		gcd->active_context = 0;
+		gcd->state = STATE_IDLE;
+
+		g_at_chat_resume(gcd->chat);
+	}
+}
+
+static int ifx_gprs_context_probe(struct ofono_gprs_context *gc,
+					unsigned int vendor, void *data)
+{
+	GAtChat *chat = data;
+	struct gprs_context_data *gcd;
+	struct stat st;
+
+	if (stat(TUN_SYSFS_DIR, &st) < 0) {
+		ofono_error("Missing support for TUN/TAP devices");
+		return -ENODEV;
+	}
+
+	if (!g_at_chat_get_slave(chat))
+		return -EINVAL;
+
+	gcd = g_try_new0(struct gprs_context_data, 1);
+	if (!gcd)
+		return -ENOMEM;
+
+	gcd->chat = g_at_chat_clone(chat);
+
+	ofono_gprs_context_set_data(gc, gcd);
+
+	chat = g_at_chat_get_slave(gcd->chat);
+
+	g_at_chat_register(chat, "+CGEV:", cgev_notify, FALSE, gc, NULL);
+
+	return 0;
+}
+
+static void ifx_gprs_context_remove(struct ofono_gprs_context *gc)
+{
+	struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+	DBG("");
+
+	if (gcd->state != STATE_IDLE && gcd->rawip) {
+		g_at_rawip_unref(gcd->rawip);
+		g_at_chat_resume(gcd->chat);
+	}
+
+	ofono_gprs_context_set_data(gc, NULL);
+
+	g_at_chat_unref(gcd->chat);
+	g_free(gcd);
+}
+
+static struct ofono_gprs_context_driver driver = {
+	.name			= "ifxmodem",
+	.probe			= ifx_gprs_context_probe,
+	.remove			= ifx_gprs_context_remove,
+	.activate_primary	= ifx_gprs_activate_primary,
+	.deactivate_primary	= ifx_gprs_deactivate_primary,
+};
+
+void ifx_gprs_context_init()
+{
+	ofono_gprs_context_driver_register(&driver);
+}
+
+void ifx_gprs_context_exit()
+{
+	ofono_gprs_context_driver_unregister(&driver);
+}
--- drivers/ifxmodem/ifxmodem.c
+++ drivers/ifxmodem/ifxmodem.c
@@ -37,6 +37,7 @@
 	ifx_voicecall_init();
 	ifx_audio_settings_init();
 	ifx_radio_settings_init();
+	ifx_gprs_context_init();
 	ifx_stk_init();
 
 	return 0;
@@ -45,6 +46,7 @@
 static void ifxmodem_exit(void)
 {
 	ifx_stk_exit();
+	ifx_gprs_context_exit();
 	ifx_radio_settings_exit();
 	ifx_audio_settings_exit();
 	ifx_voicecall_exit();
--- drivers/ifxmodem/ifxmodem.h
+++ drivers/ifxmodem/ifxmodem.h
@@ -30,5 +30,8 @@
 extern void ifx_radio_settings_init();
 extern void ifx_radio_settings_exit();
 
+extern void ifx_gprs_context_init();
+extern void ifx_gprs_context_exit();
+
 extern void ifx_stk_init();
 extern void ifx_stk_exit();
--- drivers/ifxmodem/stk.c
+++ drivers/ifxmodem/stk.c
@@ -46,7 +46,7 @@
 
 static const char *none_prefix[] = { NULL };
 static const char *sate_prefix[] = { "+SATE:", NULL };
-static const char *stkprof_prefix[] = { "+STKPROF:", NULL };
+static const char *cfun_prefix[] = { "+CFUN:", NULL };
 
 static void sate_cb(gboolean ok, GAtResult *result, gpointer user_data)
 {
@@ -203,6 +203,8 @@
 
 	DBG("");
 
+	g_at_result_iter_init(&iter, result);
+
 	if (g_at_result_iter_next(&iter, "+SATN:") == FALSE)
 		return;
 
@@ -238,10 +240,11 @@
 
 	DBG("sw1 %d sw2 %d", sw1, sw2);
 
-	ofono_stk_proactive_session_end_notify(stk);
+	if (sw1 == 0x90 && sw2 == 0x00)
+		ofono_stk_proactive_session_end_notify(stk);
 }
 
-static void stkprof_support_cb(gboolean ok, GAtResult *result,
+static void cfun_support_cb(gboolean ok, GAtResult *result,
 						gpointer user_data)
 {
 	struct ofono_stk *stk = user_data;
@@ -256,11 +259,8 @@
 	g_at_chat_register(sd->chat, "+SATN:", satn_notify, FALSE, stk, NULL);
 	g_at_chat_register(sd->chat, "+SATF:", satf_notify, FALSE, stk, NULL);
 
-	g_at_chat_send(sd->chat, "AT+STKPROF?", stkprof_prefix,
-							NULL, NULL, NULL);
-
-	g_at_chat_send(sd->chat, "AT+STKPROF=4,\"1F7F\"", none_prefix,
-							NULL, NULL, NULL);
+	g_at_chat_send(sd->chat, "AT+CFUN=6", none_prefix,
+						NULL, NULL, NULL);
 
 	ofono_stk_register(stk);
 }
@@ -280,8 +280,8 @@
 
 	ofono_stk_set_data(stk, sd);
 
-	g_at_chat_send(sd->chat, "AT+STKPROF=?", stkprof_prefix,
-					stkprof_support_cb, stk, NULL);
+	g_at_chat_send(sd->chat, "AT+CFUN=?", cfun_prefix,
+					cfun_support_cb, stk, NULL);
 
 	return 0;
 }
--- drivers/ifxmodem/voicecall.c
+++ drivers/ifxmodem/voicecall.c
@@ -494,16 +494,15 @@
 	if (!cbd)
 		goto error;
 
-	/* strlen("+VTS=\"T\";") = 9 + initial AT + null */
-	buf = g_try_new(char, len * 9 + 3);
-
+	/* strlen("+VTS=T\;") = 7 + initial AT + null */
+	buf = g_try_new(char, len * 7 + 3);
 	if (!buf)
 		goto error;
 
-	s = sprintf(buf, "AT+VTS=\"%c\"", dtmf[0]);
+	s = sprintf(buf, "AT+VTS=%c", dtmf[0]);
 
 	for (i = 1; i < len; i++)
-		s += sprintf(buf + s, ";+VTS=\"%c\"", dtmf[i]);
+		s += sprintf(buf + s, ";+VTS=%c", dtmf[i]);
 
 	s = g_at_chat_send(vd->chat, buf, none_prefix,
 				vts_cb, cbd, g_free);
--- drivers/isimodem/call-barring.c
+++ drivers/isimodem/call-barring.c
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  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.
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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.
+ *  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.
  *
- * 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
+ *  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
  *
  */
 
@@ -128,10 +128,14 @@
 	DBG("lock code %s enable %d class %d password %s\n",
 		lock, enable, cls, passwd);
 
-	if (cbd && g_isi_request_make(bd->client, msg, sizeof(msg), SS_TIMEOUT,
-					set_resp_cb, cbd))
+	if (!cbd || !bd)
+		goto error;
+
+	if (g_isi_request_make(bd->client, msg, sizeof(msg), SS_TIMEOUT,
+				set_resp_cb, cbd))
 		return;
 
+error:
 	CALLBACK_WITH_FAILURE(cb, data);
 	g_free(cbd);
 }
@@ -271,10 +275,14 @@
 
 	DBG("barring query lock code %s\n", lock);
 
-	if (cbd && g_isi_request_make(bd->client, msg, sizeof(msg), SS_TIMEOUT,
-					query_resp_cb, cbd))
+	if (!cbd || !bd)
+		goto error;
+
+	if (g_isi_request_make(bd->client, msg, sizeof(msg), SS_TIMEOUT,
+				query_resp_cb, cbd))
 		return;
 
+error:
 	CALLBACK_WITH_FAILURE(cb, 0, data);
 	g_free(cbd);
 }
@@ -338,11 +346,14 @@
 	DBG("lock code %s (%u) old password %s new password %s\n",
 		lock, ss_code, old_passwd, new_passwd);
 
-	if (cbd &&
-		g_isi_request_make(bd->client, msg, sizeof(msg), SS_TIMEOUT,
+	if (!cbd || !bd)
+		goto error;
+
+	if (g_isi_request_make(bd->client, msg, sizeof(msg), SS_TIMEOUT,
 				set_passwd_resp_cb, cbd))
 		return;
 
+error:
 	CALLBACK_WITH_FAILURE(cb, data);
 	g_free(cbd);
 }
--- drivers/isimodem/call-forwarding.c
+++ drivers/isimodem/call-forwarding.c
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  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.
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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.
+ *  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.
  *
- * 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
+ *  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
  *
  */
 
@@ -229,7 +229,7 @@
 
 	DBG("forwarding type %d class %d\n", type, cls);
 
-	if (!cbd || !number->number || strlen(number->number) > 28)
+	if (!cbd || !fd || !number->number || strlen(number->number) > 28)
 		goto error;
 
 	ss_code = forw_type_to_isi_code(type);
@@ -349,7 +349,7 @@
 
 	DBG("forwarding type %d class %d\n", type, cls);
 
-	if (!cbd)
+	if (!cbd || !fd)
 		goto error;
 
 	ss_code = forw_type_to_isi_code(type);
@@ -481,7 +481,7 @@
 
 	DBG("forwarding type %d class %d\n", type, cls);
 
-	if (!cbd || cls != 7)
+	if (!cbd || !fd || cls != 7)
 		goto error;
 
 	ss_code = forw_type_to_isi_code(type);
--- drivers/isimodem/call-meter.c
+++ drivers/isimodem/call-meter.c
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- drivers/isimodem/call-settings.c
+++ drivers/isimodem/call-settings.c
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  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.
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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.
+ *  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.
  *
- * 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
+ *  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
  *
  */
 
@@ -177,7 +177,7 @@
 
 	DBG("waiting class %d\n", cls);
 
-	if (!cbd)
+	if (!cbd || !sd)
 		goto error;
 
 	if (g_isi_request_make(sd->client, msg, sizeof(msg), SS_TIMEOUT,
@@ -268,7 +268,7 @@
 
 	DBG("waiting mode %d class %d\n", mode, cls);
 
-	if (!cbd)
+	if (!cbd || !sd)
 		goto error;
 
 	if (g_isi_request_make(sd->client, msg, sizeof(msg), SS_TIMEOUT,
--- drivers/isimodem/call.h
+++ drivers/isimodem/call.h
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- drivers/isimodem/cbs.c
+++ drivers/isimodem/cbs.c
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- drivers/isimodem/debug.c
+++ drivers/isimodem/debug.c
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- drivers/isimodem/debug.h
+++ drivers/isimodem/debug.h
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- drivers/isimodem/devinfo.c
+++ drivers/isimodem/devinfo.c
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
@@ -128,7 +128,7 @@
 		INFO_PRODUCT_MANUFACTURER
 	};
 
-	if (!cbd)
+	if (!cbd || !dev)
 		goto error;
 
 	if (g_isi_request_make(dev->client, msg, sizeof(msg),
@@ -177,7 +177,7 @@
 		0x00, 0x00, 0x00, 0x00
 	};
 
-	if (!cbd)
+	if (!cbd || !dev)
 		goto error;
 
 	if (g_isi_request_make(dev->client, msg, sizeof(msg),
@@ -201,7 +201,7 @@
 		INFO_SN_IMEI_PLAIN
 	};
 
-	if (!cbd)
+	if (!cbd || !dev)
 		goto error;
 
 	if (g_isi_request_make(dev->client, msg, sizeof(msg),
--- drivers/isimodem/gpds.h
+++ drivers/isimodem/gpds.h
@@ -1,20 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- drivers/isimodem/gprs-context.c
+++ drivers/isimodem/gprs-context.c
@@ -1,20 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *  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.
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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.
+ *  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.
  *
- * 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
+ *  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
  *
  */
 
@@ -514,7 +515,7 @@
 	struct context_data *cd = g_try_new0(struct context_data, 1);
 	struct context_data *old = NULL;
 
-	if (!cd)
+	if (!gcd || !cd)
 		return;
 
 	cd->cid = ctx->cid;
@@ -598,6 +599,9 @@
 		0x00,	/* GPDS context ID, added later */
 	};
 
+	if (!gcd)
+		return;
+
 	cd = find_context_by_cid(gcd->contexts, cid);
 	if (!cd) {
 		DBG("unknown context: %u", cid);
--- drivers/isimodem/gprs.c
+++ drivers/isimodem/gprs.c
@@ -1,20 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2010  Nokia Corporation and/or its subsidiary(-ies).
+ *  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.
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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.
+ *  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.
  *
- * 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
+ *  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
  *
  */
 
@@ -285,7 +286,7 @@
 
 	GIsiRequest *req;
 
-	if (!cbd)
+	if (!cbd || !gd)
 		goto error;
 
 	if (attached)
@@ -355,7 +356,7 @@
 		GPDS_STATUS_REQ,
 	};
 
-	if (!cbd)
+	if (!cbd || !gd)
 		goto error;
 
 	if (g_isi_send(gd->client, msg, sizeof(msg), GPDS_TIMEOUT,
--- drivers/isimodem/gss.h
+++ drivers/isimodem/gss.h
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- drivers/isimodem/info.h
+++ drivers/isimodem/info.h
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- drivers/isimodem/infoserver.c
+++ drivers/isimodem/infoserver.c
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- drivers/isimodem/infoserver.h
+++ drivers/isimodem/infoserver.h
@@ -1,20 +1,22 @@
 /*
- * oFono - Open Telephony stack for Linux
  *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
- * 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
  */
 
 #ifndef __OFONO_ISI_INFOSERVER_H
--- drivers/isimodem/isimodem.c
+++ drivers/isimodem/isimodem.c
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- drivers/isimodem/isimodem.h
+++ drivers/isimodem/isimodem.h
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- drivers/isimodem/isiutil.h
+++ drivers/isimodem/isiutil.h
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
@@ -34,7 +34,6 @@
 	struct isi_cb_data *ret;
 
 	ret = g_try_new0(struct isi_cb_data, 1);
-
 	if (ret) {
 		ret->cb = cb;
 		ret->data = data;
--- drivers/isimodem/mtc.h
+++ drivers/isimodem/mtc.h
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- drivers/isimodem/network-registration.c
+++ drivers/isimodem/network-registration.c
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Aki Niemi <aki.niemi at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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 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.
+ *  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
+ *  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
  *
  */
 
@@ -50,7 +48,6 @@
 	guint8 last_reg_mode;
 	guint8 rat;
 	guint8 gsm_compact;
-	guint8 strength;
 };
 
 static inline guint8 *mccmnc_to_bcd(const char *mcc, const char *mnc,
@@ -201,13 +198,6 @@
 	if (decode_reg_status(nd, msg+3, len-3, &status, &lac, &ci, &tech)) {
 		status = isi_status_to_at_status(status);
 		ofono_netreg_status_notify(netreg, status, lac, ci, tech);
-
-		/*
-		 * Make sure the core is also informed of current
-		 * signal strength, as it can be received before
-		 * registration status.
-		 */
-		ofono_netreg_strength_notify(netreg, nd->strength);
 	}
 }
 
@@ -265,7 +255,7 @@
 		NET_REG_STATUS_GET_REQ
 	};
 
-	if (!cbd)
+	if (!cbd || !nd)
 		goto error;
 
 	if (g_isi_request_make(nd->client, msg, sizeof(msg),
@@ -370,7 +360,7 @@
 		0x00  /* No sub-blocks */
 	};
 
-	if (!cbd)
+	if (!cbd || !nd)
 		goto error;
 
 	if (g_isi_request_make(nd->client, msg, sizeof(msg),
@@ -492,7 +482,7 @@
 		0x00
 	};
 
-	if (!cbd)
+	if (!cbd || !net)
 		goto error;
 
 	if (g_isi_request_make(net->client, msg, sizeof(msg),
@@ -558,7 +548,7 @@
 		0x00  /* Index not used */
 	};
 
-	if (!cbd)
+	if (!cbd || !net)
 		goto error;
 
 	if (g_isi_request_make(net->client, msg, sizeof(msg),
@@ -631,7 +621,7 @@
 		0x00, 0x00  /* Filler */
 	};
 
-	if (!cbd)
+	if (!cbd || !nd)
 		goto error;
 
 	if (g_isi_request_make(nd->client, msg, sizeof(msg),
@@ -756,12 +746,10 @@
 {
 	const unsigned char *msg = data;
 	struct ofono_netreg *netreg = opaque;
-	struct netreg_data *nd = ofono_netreg_get_data(netreg);
 
 	if (!msg || len < 3 || msg[0] != NET_RSSI_IND)
 		return;
 
-	nd->strength = msg[1];
 	ofono_netreg_strength_notify(netreg, msg[1]);
 }
 
@@ -878,7 +866,7 @@
 		NET_CURRENT_CELL_RSSI
 	};
 
-	if (!cbd)
+	if (!cbd || !nd)
 		goto error;
 
 	if (g_isi_request_make(nd->client, msg, sizeof(msg),
--- drivers/isimodem/network.h
+++ drivers/isimodem/network.h
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- drivers/isimodem/phonebook.c
+++ drivers/isimodem/phonebook.c
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Alexander Kanavin <alexander.kanavin at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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 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.
+ *  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
+ *  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
  *
  */
 
@@ -261,7 +259,7 @@
 		0, 0				/* filler */
 	};
 
-	if (!cbd)
+	if (!cbd || !pbd)
 		goto error;
 
 	if (strcmp(storage, "SM"))
@@ -337,10 +335,12 @@
 {
 	struct pb_data *data = ofono_phonebook_get_data(pb);
 
-	if (data) {
-		g_isi_client_destroy(data->client);
-		g_free(data);
-	}
+	if (!data)
+		return;
+
+	ofono_phonebook_set_data(pb, NULL);
+	g_isi_client_destroy(data->client);
+	g_free(data);
 }
 
 static struct ofono_phonebook_driver driver = {
--- drivers/isimodem/radio-settings.c
+++ drivers/isimodem/radio-settings.c
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *  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.
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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.
+ *  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.
  *
- * 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
+ *  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
  *
  */
 
@@ -154,7 +154,7 @@
 		0x00 /* subblock count */
 	};
 
-	if (!cbd)
+	if (!cbd || !rd)
 		goto error;
 
 	if (g_isi_request_make(rd->client, msg, sizeof(msg), GSS_TIMEOUT,
@@ -221,7 +221,7 @@
 		0x00 /* filler */
 	};
 
-	if (!cbd)
+	if (!cbd || !rd)
 		goto error;
 
 	if (isi_mode == -1)
@@ -297,9 +297,11 @@
 {
 	struct radio_data *rd = ofono_radio_settings_get_data(rs);
 
-	if (rd->client)
-		g_isi_client_destroy(rd->client);
+	if (!rd)
+		return;
 
+	ofono_radio_settings_set_data(rs, NULL);
+	g_isi_client_destroy(rd->client);
 	g_free(rd);
 }
 
--- drivers/isimodem/sim.c
+++ drivers/isimodem/sim.c
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  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.
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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.
+ *  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.
  *
- * 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
+ *  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
  *
  */
 
@@ -54,6 +54,7 @@
 	int structure;
 	int record_length;
 	unsigned char access[3];
+	unsigned char file_status;
 };
 
 /* Returns file info */
@@ -66,7 +67,7 @@
 	DBG("Returning static file_info for %04x", fi->fileid);
 	CALLBACK_WITH_SUCCESS(cb,
 				fi->length, fi->structure, fi->record_length,
-				fi->access, cbd->data);
+				fi->access, fi->file_status, cbd->data);
 	g_free(cbd);
 	return FALSE;
 }
@@ -76,8 +77,8 @@
 {
 	int i;
 	static struct file_info const info[] = {
-		{ SIM_EFSPN_FILEID, 17, 0, 0, { 0x0f, 0xff, 0xff } },
-		{ SIM_EF_ICCID_FILEID, 10, 0, 0, { 0x0f, 0xff, 0xff } },
+		{ SIM_EFSPN_FILEID, 17, 0, 0, { 0x0f, 0xff, 0xff }, 1 },
+		{ SIM_EF_ICCID_FILEID, 10, 0, 0, { 0x0f, 0xff, 0xff }, 1 },
 	};
 	int N = sizeof(info) / sizeof(info[0]);
 	struct isi_cb_data *cbd;
@@ -91,7 +92,7 @@
 	}
 
 	DBG("Not implemented (fileid = %04x)", fileid);
-	CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, data);
+	CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, 0, data);
 }
 
 static gboolean spn_resp_cb(GIsiClient *client,
@@ -155,6 +156,9 @@
 		0
 	};
 
+	if (!sd)
+		return FALSE;
+
 	return g_isi_request_make(sd->client, msg, sizeof(msg),
 					SIM_TIMEOUT, spn_resp_cb, cbd) != NULL;
 }
@@ -196,6 +200,9 @@
 	struct sim_data *sd = ofono_sim_get_data(sim);
 	const unsigned char req[] = { SIM_READ_FIELD_REQ, ICC };
 
+	if (!sd)
+		return FALSE;
+
 	return g_isi_request_make(sd->client, req, sizeof(req), SIM_TIMEOUT,
 					read_iccid_resp_cb, cbd) != NULL;
 }
@@ -336,11 +343,10 @@
 		READ_IMSI
 	};
 
-	if (!cbd)
+	if (!cbd || !sd)
 		goto error;
 
-	if (g_isi_request_make(sd->client, msg, sizeof(msg),
-				SIM_TIMEOUT,
+	if (g_isi_request_make(sd->client, msg, sizeof(msg), SIM_TIMEOUT,
 				imsi_resp_cb, cbd))
 		return;
 
--- drivers/isimodem/sim.h
+++ drivers/isimodem/sim.h
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- drivers/isimodem/sms.c
+++ drivers/isimodem/sms.c
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *  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.
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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.
+ *  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.
  *
- * 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
+ *  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
  *
  */
 
@@ -49,6 +49,18 @@
 struct sms_data {
 	GIsiClient *client;
 	GIsiClient *sim;
+	/* This is a straightforward copy of the EF_smsp structure */
+	struct sim_parameters {
+		uint8_t      absent;
+		uint8_t      tp_pid;
+		uint8_t      tp_dcs;
+		uint8_t      tp_vp;
+		uint8_t      dst[12];
+		uint8_t      sca[12];
+		uint8_t      alphalen;
+		uint8_t      filler[3];
+		uint16_t     alpha[17];
+	} params;
 };
 
 static gboolean sca_query_resp_cb(GIsiClient *client,
@@ -57,12 +69,27 @@
 {
 	const uint8_t *msg = data;
 	struct isi_cb_data *cbd = opaque;
+	struct ofono_sms *sms = cbd->user;
+	struct sms_data *sd = ofono_sms_get_data(sms);
 	ofono_sms_sca_query_cb_t cb = cbd->cb;
 
 	struct ofono_phone_number sca;
-	const uint8_t *bcd;
 	uint8_t bcd_len;
 
+	/* Nicely aligned. */
+	struct {
+		uint8_t      status;
+		uint8_t      absent;
+		uint8_t      tp_pid;
+		uint8_t      tp_dcs;
+		uint8_t      dst[12];
+		uint8_t      sca[12];
+		uint8_t      tp_vp;
+		uint8_t      alphalen;
+		uint8_t      filler[2];
+		uint16_t     alpha[17];
+	} params;
+
 	if (!msg) {
 		DBG("ISI client error: %d", g_isi_client_error(client));
 		goto error;
@@ -74,28 +101,46 @@
 	if (msg[3] != SIM_SERV_OK)
 		goto error;
 
-	/* Bitmask indicating presence of parameters -- second flag
-	 * set is an indicator that the SCA is absent */
-	if (msg[4] & 0x2)
+	memset(&params, 0, sizeof(params));
+	if (len  > 3 + sizeof(params))
+		len = 3 + sizeof(params);
+	memcpy(&params, msg + 3, len - 3);
+
+	if (params.alphalen > 17)
+		params.alphalen = 17;
+	else if (params.alphalen < 1)
+		params.alphalen = 1;
+	params.alpha[params.alphalen - 1] = '\0';
+
+	sd->params.absent = params.absent;
+	sd->params.tp_pid = params.tp_pid;
+	sd->params.tp_dcs = params.tp_dcs;
+	sd->params.tp_vp = params.tp_vp;
+	memcpy(sd->params.dst, params.dst, sizeof(sd->params.dst));
+	memcpy(sd->params.sca, params.sca, sizeof(sd->params.sca));
+	sd->params.alphalen = params.alphalen;
+	memcpy(sd->params.alpha, params.alpha, sizeof(sd->params.alpha));
+
+	/*
+	 * Bitmask indicating absense of parameters --
+	 * If second bit is set it indicates that the SCA is absent
+	 */
+	if (params.absent & 0x2)
 		goto error;
 
-	bcd = msg + 19;
-	bcd_len = bcd[0];
+	bcd_len = params.sca[0];
 
 	if (bcd_len <= 1 || bcd_len > 12)
 		goto error;
 
-	extract_bcd_number(bcd + 2, bcd_len - 1, sca.number);
-	sca.type = bcd[1];
+	extract_bcd_number(params.sca + 2, bcd_len - 1, sca.number);
+	sca.type = 0x80 | params.sca[1];
 
 	CALLBACK_WITH_SUCCESS(cb, &sca, cbd->data);
-	goto out;
+	return TRUE;
 
 error:
 	CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
-
-out:
-	g_free(cbd);
 	return TRUE;
 }
 
@@ -111,11 +156,11 @@
 		1,	/* Location, default is 1 */
 	};
 
-	if (!cbd)
+	if (!cbd || !sd)
 		goto error;
 
-	if (g_isi_request_make(sd->sim, msg, sizeof(msg), SIM_TIMEOUT,
-				sca_query_resp_cb, cbd))
+	if (g_isi_send(sd->sim, msg, sizeof(msg), SIM_TIMEOUT,
+			sca_query_resp_cb, cbd, g_free))
 		return;
 
 error:
@@ -143,13 +188,10 @@
 		goto error;
 
 	CALLBACK_WITH_SUCCESS(cb, cbd->data);
-	goto out;
+	return TRUE;
 
 error:
 	CALLBACK_WITH_FAILURE(cb, cbd->data);
-
-out:
-	g_free(cbd);
 	return TRUE;
 }
 
@@ -159,33 +201,31 @@
 {
 	struct sms_data *sd = ofono_sms_get_data(sms);
 	struct isi_cb_data *cbd = isi_cb_data_new(sms, cb, data);
+	uint8_t *bcd;
 
 	uint8_t msg[] = {
 		SIM_SMS_REQ,
 		UPDATE_PARAMETER,
 		1,	/* Location, default is 1 */
-		0xFD,	/* Params present, only SCA */
 	};
 
-	uint8_t filler[40];
-	uint8_t bcd[12];
-
-	struct iovec iov[4] = {
+	struct iovec iov[] = {
 		{ msg, sizeof(msg) },
-		{ filler, 15 },
-		{ bcd, sizeof(bcd) },
-		{ filler, 38 },
+		{ &sd->params, sizeof(sd->params) },
 	};
 
-	if (!cbd)
+	if (!cbd || !sd)
 		goto error;
 
+	bcd = sd->params.sca;
+	sd->params.absent &= ~0x02;
+
 	encode_bcd_number(sca->number, bcd + 2);
 	bcd[0] = 1 + (strlen(sca->number) + 1) / 2;
-	bcd[1] = sca->type;
+	bcd[1] = sca->type & 0xFF;
 
-	if (g_isi_request_vmake(sd->sim, iov, 4, SIM_TIMEOUT,
-				sca_set_resp_cb, cbd))
+	if (g_isi_vsend(sd->sim, iov, G_N_ELEMENTS(iov), SIM_TIMEOUT,
+			sca_set_resp_cb, cbd, g_free))
 		return;
 
 error:
@@ -253,13 +293,10 @@
 		goto error;
 
 	CALLBACK_WITH_SUCCESS(cb, mr, cbd->data);
-	goto out;
+	return TRUE;
 
 error:
 	CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
-
-out:
-	g_free(cbd);
 	return TRUE;
 }
 
@@ -270,14 +307,13 @@
 	struct sms_data *sd = ofono_sms_get_data(sms);
 	struct isi_cb_data *cbd = isi_cb_data_new(sms, cb, data);
 
-	uint8_t *sca = pdu;
-	uint8_t sca_len = pdu_len - tpdu_len;
-	uint8_t sca_sb_len = 4 + sca_len;
+	uint8_t use_sca = pdu_len - tpdu_len != 1 || pdu[0] == 0;
 
-	uint8_t *tpdu = pdu + sca_len;
-	uint8_t ud_sb_len = 4 + tpdu_len;
+	uint8_t *tpdu = pdu + pdu_len - tpdu_len;
+	uint8_t filler_len = (-tpdu_len) & 3;
+	uint8_t tpdu_sb_len = 4 + tpdu_len + filler_len;
 
-	uint8_t use_default = sca_len == 1 && sca[0] == 0;
+	uint8_t sca_sb_len = use_sca ? 16 : 0;
 
 	uint8_t msg[] = {
 		SMS_MESSAGE_SEND_REQ,
@@ -288,36 +324,44 @@
 		SMS_TYPE_TEXT_MESSAGE,
 		1,	/* Sub blocks */
 		SMS_GSM_TPDU,
-		4 + ud_sb_len + (use_default ? 0 : sca_sb_len),
+		4 + tpdu_sb_len + sca_sb_len,
 		0,	/* Filler */
-		1 + (use_default ? 0 : 1), /* Sub blocks */
+		use_sca ? 2 : 1,	/* Sub-sub blocks */
 		SMS_COMMON_DATA,
-		ud_sb_len,
+		tpdu_sb_len,
 		tpdu_len,
 		0,	/* Packing required? */
 		/* TPDU */
 	};
 
-	uint8_t scaddr[] = {
+	static uint8_t filler[4];
+
+	uint8_t sca_sb[16] = {
 		SMS_ADDRESS,
-		sca_sb_len,
+		16,
 		SMS_GSM_0411_ADDRESS,
-		sca_len,
-		/* SCA */
+		0,
 	};
 
 	struct iovec iov[4] = {
 		{ msg, sizeof(msg) },
 		{ tpdu, tpdu_len },
-		{ scaddr, sizeof(scaddr) },
-		{ sca, sca_len },
+		{ filler, filler_len },
+		{ sca_sb, sca_sb_len },
 	};
 
-	if (!cbd)
+	if (!cbd || !sd)
 		goto error;
 
-	if (g_isi_request_vmake(sd->client, iov, use_default ? 2 : 4, SMS_TIMEOUT,
-				submit_resp_cb, cbd))
+	if (use_sca) {
+		sca_sb[3] = pdu_len - tpdu_len;
+		memcpy(sca_sb + 4, pdu, sca_sb[3]);
+	}
+
+	/* Modem seems to time out SMS_MESSAGE_SEND_REQ in 5 seconds */
+	/* Wait normal timeout plus the modem timeout */
+	if (g_isi_vsend(sd->client, iov, G_N_ELEMENTS(iov), SMS_TIMEOUT + 5,
+			submit_resp_cb, cbd, g_free))
 		return;
 
 error:
@@ -356,6 +400,7 @@
 		return FALSE;
 
 	DBG("Report resp cause=0x%"PRIx8, msg[1]);
+
 	return TRUE;
 }
 
@@ -378,8 +423,8 @@
 		0,		/* Sub blocks */
 	};
 
-	return g_isi_request_make(client, msg, sizeof(msg), SMS_TIMEOUT,
-					report_resp_cb, NULL) != NULL;
+	return g_isi_send(client, msg, sizeof(msg), SMS_TIMEOUT,
+				report_resp_cb, NULL, NULL) != NULL;
 }
 
 static void routing_ntf_cb(GIsiClient *client,
@@ -518,13 +563,16 @@
 	if (!data)
 		return -ENOMEM;
 
+	data->params.absent = 0xff;
+	data->params.alphalen = 1; /* Includes final UCS2-coded NUL */
+
 	data->client = g_isi_client_create(idx, PN_SMS);
 	if (!data->client)
 		return -ENOMEM;
 
 	data->sim = g_isi_client_create(idx, PN_SIM);
 	if (!data->sim) {
-		g_free(data->client);
+		g_isi_client_destroy(data->client);
 		return -ENOMEM;
 	}
 
@@ -538,8 +586,8 @@
 
 	g_isi_subscribe(data->client, SMS_MESSAGE_SEND_STATUS_IND,
 			send_status_ind_cb, sms);
-	if (!g_isi_request_make(data->client, msg, sizeof(msg), SMS_TIMEOUT,
-				routing_resp_cb, sms))
+	if (!g_isi_send(data->client, msg, sizeof(msg), SMS_TIMEOUT,
+			routing_resp_cb, sms, NULL))
 		DBG("Failed to set SMS routing.");
 
 	return 0;
@@ -564,17 +612,16 @@
 	if (!data)
 		return;
 
-	if (data->client) {
-		/* Send a promiscuous routing release, so as not to
-		 * hog resources unnecessarily after being removed */
-		g_isi_request_make(data->client, msg, sizeof(msg),
-					SMS_TIMEOUT, NULL, NULL);
-		g_isi_client_destroy(data->client);
-	}
-
-	if (data->sim)
-		g_isi_client_destroy(data->sim);
+	ofono_sms_set_data(sms, NULL);
 
+	/*
+	 * Send a promiscuous routing release, so as not to
+	 * hog resources unnecessarily after being removed
+	 */
+	g_isi_send(data->client, msg, sizeof(msg),
+			SMS_TIMEOUT, NULL, NULL, NULL);
+	g_isi_client_destroy(data->client);
+	g_isi_client_destroy(data->sim);
 	g_free(data);
 }
 
--- drivers/isimodem/sms.h
+++ drivers/isimodem/sms.h
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- drivers/isimodem/ss.h
+++ drivers/isimodem/ss.h
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Alexander Kanavin <alexander.kanavin at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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
+ *  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
  *
  */
 
--- drivers/isimodem/ssn.c
+++ drivers/isimodem/ssn.c
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Aki Niemi <aki.niemi at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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
+ *  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
  *
  */
 
@@ -69,10 +67,12 @@
 {
 	struct ssn_data *data = ofono_ssn_get_data(ssn);
 
-	if (data) {
-		g_isi_client_destroy(data->client);
-		g_free(data);
-	}
+	if (!data)
+		return;
+
+	ofono_ssn_set_data(ssn, NULL);
+	g_isi_client_destroy(data->client);
+	g_free(data);
 }
 
 static struct ofono_ssn_driver driver = {
--- drivers/isimodem/ussd.c
+++ drivers/isimodem/ussd.c
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Aki Niemi <aki.niemi at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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
+ *  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
  *
  */
 
@@ -183,7 +181,7 @@
 		{ (uint8_t *)pdu, len }
 	};
 
-	if (!cbd)
+	if (!cbd || !ud)
 		goto error;
 
 	if (g_isi_vsend(ud->client, iov, 2, SS_TIMEOUT,
@@ -207,7 +205,7 @@
 		0x00		/* subblock count */
 	};
 
-	if (!cbd)
+	if (!cbd || !ud)
 		goto error;
 
 	if (g_isi_send(ud->client, msg, sizeof(msg), SS_TIMEOUT,
--- drivers/isimodem/voicecall.c
+++ drivers/isimodem/voicecall.c
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Pekka Pessi <Pekka.Pessi at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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 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.
+ *  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
+ *  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
  *
  */
 
@@ -904,9 +902,9 @@
 		return 4;
 	case CALL_STATUS_PROCEEDING:
 		if ((call->mode_info & CALL_MODE_ORIGINATOR))
-			return 4;
+			return 4; /* MT */
 		else
-			return 2;
+			return 2; /* MO */
 	case CALL_STATUS_MO_ALERTING:
 		return 3;
 	case CALL_STATUS_MT_ALERTING:
@@ -988,11 +986,37 @@
 	isi_call_answer_req(ovc, CALL_ID_ALL, cb, data);
 }
 
-static void isi_hangup(struct ofono_voicecall *ovc,
+static void isi_hangup_current(struct ofono_voicecall *ovc,
 			ofono_voicecall_cb_t cb, void *data)
 {
-	/* AT+CHUP */
-	isi_call_release_req(ovc, CALL_ID_ALL, CALL_CAUSE_TYPE_CLIENT,
+	/*
+	 * Hangup call(s) that are not held or waiting:
+	 * active calls or calls in progress.
+	 */
+	struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+	int id = 0;
+
+	for (id = 1; id <= 7; id++) {
+		if (ivc->calls[id].call_id & CALL_ID_WAITING)
+			continue;
+		if (ivc->calls[id].call_id & CALL_ID_HOLD)
+			continue;
+
+		switch (ivc->calls[id].status) {
+		case CALL_STATUS_CREATE:
+		case CALL_STATUS_COMING:
+		case CALL_STATUS_PROCEEDING:
+		case CALL_STATUS_MO_ALERTING:
+		case CALL_STATUS_MT_ALERTING:
+		case CALL_STATUS_ANSWERED:
+			goto release_by_id;
+		}
+	}
+
+	id = CALL_ID_ACTIVE;
+
+release_by_id:
+	isi_call_release_req(ovc, id, CALL_CAUSE_TYPE_CLIENT,
 				CALL_CAUSE_RELEASE_BY_USER, cb, data);
 }
 
@@ -1007,25 +1031,10 @@
 static void isi_set_udub(struct ofono_voicecall *ovc,
 				ofono_voicecall_cb_t cb, void *data)
 {
-	/* AT+CHLD=0 (w/ incoming calls) */
-	struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
-	int id = 0;
-
-	for (id = 1; id <= 7; id++) {
-		if (ivc->calls[id].status == CALL_STATUS_WAITING)
-			break;
-		if (ivc->calls[id].status == CALL_STATUS_MT_ALERTING)
-			break;
-		if (ivc->calls[id].status == CALL_STATUS_COMING)
-			break;
-	}
-
-	if (id <= 7)
-		isi_call_release_req(ovc, id, CALL_CAUSE_TYPE_CLIENT,
-					CALL_CAUSE_BUSY_USER_REQUEST,
-					cb, data);
-	else
-		CALLBACK_WITH_FAILURE(cb, data);
+	/* Release waiting calls */
+	isi_call_release_req(ovc, CALL_ID_WAITING,
+		CALL_CAUSE_TYPE_CLIENT, CALL_CAUSE_BUSY_USER_REQUEST,
+		cb, data);
 }
 
 static void isi_retrieve(struct ofono_voicecall *ovc,
@@ -1306,10 +1315,12 @@
 {
 	struct isi_voicecall *data = ofono_voicecall_get_data(call);
 
-	if (data) {
-		g_isi_client_destroy(data->client);
-		g_free(data);
-	}
+	if (!data)
+		return;
+
+	ofono_voicecall_set_data(call, NULL);
+	g_isi_client_destroy(data->client);
+	g_free(data);
 }
 
 static struct ofono_voicecall_driver driver = {
@@ -1318,7 +1329,7 @@
 	.remove			= isi_voicecall_remove,
 	.dial			= isi_dial,
 	.answer			= isi_answer,
-	.hangup_active		= isi_hangup,
+	.hangup_active		= isi_hangup_current,
 	.hold_all_active	= isi_hold_all_active,
 	.release_all_held	= isi_release_all_held,
 	.set_udub		= isi_set_udub,
--- gatchat/gatchat.c
+++ gatchat/gatchat.c
@@ -61,8 +61,12 @@
 	GAtNotifyFunc callback;
 	gpointer user_data;
 	GDestroyNotify notify;
+	gboolean destroyed;
 };
 
+typedef gboolean (*node_remove_func)(struct at_notify_node *node,
+					gpointer user_data);
+
 struct at_notify {
 	GSList *nodes;
 	gboolean pdu;
@@ -93,6 +97,7 @@
 	GAtSyntax *syntax;
 	gboolean destroyed;			/* Re-entrancy guard */
 	gboolean in_read_handler;		/* Re-entrancy guard */
+	gboolean in_notify;
 	GSList *terminator_list;		/* Non-standard terminator */
 };
 
@@ -100,6 +105,7 @@
 	gint ref_count;
 	struct at_chat *parent;
 	guint group;
+	GAtChat *slave;
 };
 
 struct terminator_info {
@@ -108,6 +114,11 @@
 	gboolean success;
 };
 
+static gboolean node_is_destroyed(struct at_notify_node *node, gpointer user)
+{
+	return node->destroyed;
+}
+
 static gint at_notify_node_compare_by_id(gconstpointer a, gconstpointer b)
 {
 	const struct at_notify_node *node = a;
@@ -154,6 +165,65 @@
 	return 0;
 }
 
+static gboolean at_chat_unregister_all(struct at_chat *chat,
+					gboolean mark_only,
+					node_remove_func func,
+					gpointer userdata)
+{
+	GHashTableIter iter;
+	struct at_notify *notify;
+	struct at_notify_node *node;
+	gpointer key, value;
+	GSList *p;
+	GSList *c;
+	GSList *t;
+
+	if (chat->notify_list == NULL)
+		return FALSE;
+
+	g_hash_table_iter_init(&iter, chat->notify_list);
+
+	while (g_hash_table_iter_next(&iter, &key, &value)) {
+		notify = value;
+
+		p = NULL;
+		c = notify->nodes;
+
+		while (c) {
+			node = c->data;
+
+			if (func(node, userdata) != TRUE) {
+				p = c;
+				c = c->next;
+				continue;
+			}
+
+			if (mark_only) {
+				node->destroyed = TRUE;
+				p = c;
+				c = c->next;
+				continue;
+			}
+
+			if (p)
+				p->next = c->next;
+			else
+				notify->nodes = c->next;
+
+			at_notify_node_destroy(node, NULL);
+
+			t = c;
+			c = c->next;
+			g_slist_free_1(t);
+		}
+
+		if (notify->nodes == NULL)
+			g_hash_table_iter_remove(&iter);
+	}
+
+	return TRUE;
+}
+
 static struct at_command *at_command_create(guint gid, const char *cmd,
 						const char **prefix_list,
 						gboolean expect_pdu,
@@ -321,6 +391,8 @@
 	result.lines = 0;
 	result.final_or_pdu = 0;
 
+	chat->in_notify = TRUE;
+
 	while (g_hash_table_iter_next(&iter, &key, &value)) {
 		notify = value;
 
@@ -344,9 +416,13 @@
 		ret = TRUE;
 	}
 
+	chat->in_notify = FALSE;
+
 	if (ret) {
 		g_slist_free(result.lines);
 		g_free(line);
+
+		at_chat_unregister_all(chat, FALSE, node_is_destroyed, NULL);
 	}
 
 	return ret;
@@ -530,6 +606,9 @@
 	struct at_notify *notify;
 	char *prefix;
 	gpointer key, value;
+	gboolean called = FALSE;
+
+	p->in_notify = TRUE;
 
 	g_hash_table_iter_init(&iter, p->notify_list);
 
@@ -544,7 +623,13 @@
 			continue;
 
 		g_slist_foreach(notify->nodes, at_notify_call_callback, result);
+		called = TRUE;
 	}
+
+	p->in_notify = FALSE;
+
+	if (called)
+		at_chat_unregister_all(p, FALSE, node_is_destroyed, NULL);
 }
 
 static void have_pdu(struct at_chat *p, char *pdu)
@@ -1060,7 +1145,8 @@
 	return node->id;
 }
 
-static gboolean at_chat_unregister(struct at_chat *chat, guint group, guint id)
+static gboolean at_chat_unregister(struct at_chat *chat, gboolean mark_only,
+					guint group, guint id)
 {
 	GHashTableIter iter;
 	struct at_notify *notify;
@@ -1085,7 +1171,12 @@
 		node = l->data;
 
 		if (node->gid != group)
-			continue;
+			return FALSE;
+
+		if (mark_only) {
+			node->destroyed = TRUE;
+			return TRUE;
+		}
 
 		at_notify_node_destroy(node, NULL);
 		notify->nodes = g_slist_remove(notify->nodes, node);
@@ -1099,53 +1190,15 @@
 	return FALSE;
 }
 
-static gboolean at_chat_unregister_group(struct at_chat *chat, guint group)
+static gboolean node_compare_by_group(struct at_notify_node *node,
+					gpointer userdata)
 {
-	GHashTableIter iter;
-	struct at_notify *notify;
-	struct at_notify_node *node;
-	gpointer key, value;
-	GSList *p;
-	GSList *c;
-	GSList *t;
-
-	if (chat->notify_list == NULL)
-		return FALSE;
-
-	g_hash_table_iter_init(&iter, chat->notify_list);
-
-	while (g_hash_table_iter_next(&iter, &key, &value)) {
-		notify = value;
-
-		p = NULL;
-		c = notify->nodes;
-
-		while (c) {
-			node = c->data;
-
-			if (node->gid != group) {
-				p = c;
-				c = c->next;
-				continue;
-			}
-
-			if (p)
-				p->next = c->next;
-			else
-				notify->nodes = c->next;
-
-			at_notify_node_destroy(node, NULL);
-
-			t = c;
-			c = c->next;
-			g_slist_free_1(t);
-		}
+	guint group = GPOINTER_TO_UINT(userdata);
 
-		if (notify->nodes == NULL)
-			g_hash_table_iter_remove(&iter);
-	}
+	if (node->gid == group)
+		return TRUE;
 
-	return TRUE;
+	return FALSE;
 }
 
 static struct at_chat *create_chat(GIOChannel *channel, GIOFlags flags,
@@ -1252,9 +1305,36 @@
 	chat->ref_count = 1;
 	g_atomic_int_inc(&chat->parent->ref_count);
 
+	if (clone->slave != NULL)
+		chat->slave = g_at_chat_clone(clone->slave);
+
 	return chat;
 }
 
+GAtChat *g_at_chat_set_slave(GAtChat *chat, GAtChat *slave)
+{
+	if (chat == NULL)
+		return NULL;
+
+	if (chat->slave != NULL)
+		g_at_chat_unref(chat->slave);
+
+	if (slave != NULL)
+		chat->slave = g_at_chat_ref(slave);
+	else
+		chat->slave = NULL;
+
+	return chat->slave;
+}
+
+GAtChat *g_at_chat_get_slave(GAtChat *chat)
+{
+	if (chat == NULL)
+		return NULL;
+
+	return chat->slave;
+}
+
 GIOChannel *g_at_chat_get_channel(GAtChat *chat)
 {
 	if (chat == NULL || chat->parent->io == NULL)
@@ -1309,8 +1389,11 @@
 	if (is_zero == FALSE)
 		return;
 
+	if (chat->slave != NULL)
+		g_at_chat_unref(chat->slave);
+
 	at_chat_cancel_group(chat->parent, chat->group);
-	at_chat_unregister_group(chat->parent, chat->group);
+	g_at_chat_unregister_all(chat);
 	at_chat_unref(chat->parent);
 
 	g_free(chat);
@@ -1423,7 +1506,8 @@
 	if (chat == NULL)
 		return FALSE;
 
-	return at_chat_unregister(chat->parent, chat->group, id);
+	return at_chat_unregister(chat->parent, chat->parent->in_notify,
+					chat->group, id);
 }
 
 gboolean g_at_chat_unregister_all(GAtChat *chat)
@@ -1431,5 +1515,8 @@
 	if (chat == NULL)
 		return FALSE;
 
-	return at_chat_unregister_group(chat->parent, chat->group);
+	return at_chat_unregister_all(chat->parent,
+					chat->parent->in_notify,
+					node_compare_by_group,
+					GUINT_TO_POINTER(chat->group));
 }
--- gatchat/gatchat.h
+++ gatchat/gatchat.h
@@ -50,6 +50,9 @@
 
 GAtChat *g_at_chat_clone(GAtChat *chat);
 
+GAtChat *g_at_chat_set_slave(GAtChat *chat, GAtChat *slave);
+GAtChat *g_at_chat_get_slave(GAtChat *chat);
+
 void g_at_chat_suspend(GAtChat *chat);
 void g_at_chat_resume(GAtChat *chat);
 
--- gatchat/gatmux.c
+++ gatchat/gatmux.c
@@ -807,16 +807,21 @@
 		goto error;
 
 	/* Speed, pick highest */
-	if (!g_at_result_iter_open_list(&iter))
-		goto error;
+	if (g_at_result_iter_open_list(&iter)) {
+		if (!g_at_result_iter_next_range(&iter, &min, &max))
+			goto error;
 
-	if (!g_at_result_iter_next_range(&iter, &min, &max))
-		goto error;
+		if (!g_at_result_iter_close_list(&iter))
+			goto error;
 
-	if (!g_at_result_iter_close_list(&iter))
-		goto error;
+		speed = max;
+	} else {
+		if (!g_at_result_iter_skip_next(&iter))
+			goto error;
 
-	speed = max;
+		/* not available/used */
+		speed = -1;
+	}
 
 	/* Frame size, pick defaults */
 	if (!g_at_result_iter_open_list(&iter))
@@ -844,7 +849,11 @@
 	nmsd = g_memdup(msd, sizeof(struct mux_setup_data));
 	g_at_chat_ref(nmsd->chat);
 
-	sprintf(buf, "AT+CMUX=%u,0,%u,%u", msd->mode, speed, msd->frame_size);
+	if (speed < 0)
+		sprintf(buf, "AT+CMUX=%u,0,,%u", msd->mode, msd->frame_size);
+	else
+		sprintf(buf, "AT+CMUX=%u,0,%u,%u", msd->mode, speed,
+							msd->frame_size);
 
 	if (g_at_chat_send(msd->chat, buf, none_prefix,
 				mux_setup_cb, nmsd, msd_free) > 0)
--- gatchat/gatrawip.c
+++ gatchat/gatrawip.c
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2010  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
+ *  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 <glib.h>
+
+#include "gatrawip.h"
+
+struct _GAtRawIP {
+	gint ref_count;
+	GAtIO *io;
+	char *ifname;
+	GAtDebugFunc debugf;
+	gpointer debug_data;
+};
+
+GAtRawIP *g_at_rawip_new(GIOChannel *channel)
+{
+	GAtRawIP *rawip;
+	GAtIO *io;
+
+	io = g_at_io_new(channel);
+	if (io == NULL)
+		return NULL;
+
+	rawip = g_at_rawip_new_from_io(io);
+
+	g_at_io_unref(io);
+
+	return rawip;
+}
+
+GAtRawIP *g_at_rawip_new_from_io(GAtIO *io)
+{
+	GAtRawIP *rawip;
+
+	rawip = g_try_new0(GAtRawIP, 1);
+	if (rawip == NULL)
+		return NULL;
+
+	rawip->ref_count = 1;
+
+	rawip->io = g_at_io_ref(io);
+
+	return rawip;
+}
+
+GAtRawIP *g_at_rawip_ref(GAtRawIP *rawip)
+{
+	if (rawip == NULL)
+		return NULL;
+
+	g_atomic_int_inc(&rawip->ref_count);
+
+	return rawip;
+}
+
+void g_at_rawip_unref(GAtRawIP *rawip)
+{
+	if (rawip == NULL)
+		return;
+
+	if (g_atomic_int_dec_and_test(&rawip->ref_count) == FALSE)
+		return;
+
+	g_at_io_unref(rawip->io);
+	rawip->io = NULL;
+
+	g_free(rawip->ifname);
+	g_free(rawip);
+}
+
+void g_at_rawip_open(GAtRawIP *rawip)
+{
+	if (rawip == NULL)
+		return;
+
+	/* open TUN/TAP device */
+}
+
+void g_at_rawip_shutdown(GAtRawIP *rawip)
+{
+	if (rawip == NULL)
+		return;
+
+	/* close TUN/TAP device */
+}
+
+const char *g_at_rawip_get_interface(GAtRawIP *rawip)
+{
+	if (rawip == NULL)
+		return NULL;
+
+	return rawip->ifname;
+}
+
+void g_at_rawip_set_debug(GAtRawIP *rawip, GAtDebugFunc func,
+						gpointer user_data)
+{
+	if (rawip == NULL)
+		return;
+
+	rawip->debugf = func;
+	rawip->debug_data = user_data;
+}
--- gatchat/gatrawip.h
+++ gatchat/gatrawip.h
+/*
+ *
+ *  AT chat library with GLib integration
+ *
+ *  Copyright (C) 2008-2010  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
+ *  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
+ *
+ */
+
+#ifndef __G_AT_RAWIP_H
+#define __G_AT_RAWIP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "gat.h"
+#include "gatio.h"
+
+struct _GAtRawIP;
+
+typedef struct _GAtRawIP GAtRawIP;
+
+GAtRawIP *g_at_rawip_new(GIOChannel *channel);
+GAtRawIP *g_at_rawip_new_from_io(GAtIO *io);
+
+GAtRawIP *g_at_rawip_ref(GAtRawIP *rawip);
+void g_at_rawip_unref(GAtRawIP *rawip);
+
+void g_at_rawip_open(GAtRawIP *rawip);
+void g_at_rawip_shutdown(GAtRawIP *rawip);
+
+const char *g_at_rawip_get_interface(GAtRawIP *rawip);
+
+void g_at_rawip_set_debug(GAtRawIP *rawip, GAtDebugFunc func,
+						gpointer user_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __G_AT_RAWIP_H */
--- gatchat/ppp_net.c
+++ gatchat/ppp_net.c
@@ -44,7 +44,7 @@
 	GAtPPP *ppp;
 	char *if_name;
 	GIOChannel *channel;
-	gint watch;
+	guint watch;
 	gint mtu;
 	struct ppp_header *ppp_packet;
 };
@@ -52,31 +52,33 @@
 gboolean ppp_net_set_mtu(struct ppp_net *net, guint16 mtu)
 {
 	struct ifreq ifr;
-	int sock;
-	int rc;
+	int sk, err;
 
 	if (net == NULL || mtu > MAX_PACKET)
 		return FALSE;
 
 	net->mtu = mtu;
 
-	sock = socket(AF_INET, SOCK_DGRAM, 0);
-	if (sock < 0)
+	sk = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sk < 0)
 		return FALSE;
 
 	memset(&ifr, 0, sizeof(ifr));
 	strncpy(ifr.ifr_name, net->if_name, sizeof(ifr.ifr_name));
 	ifr.ifr_mtu = mtu;
 
-	rc = ioctl(sock, SIOCSIFMTU, (caddr_t) &ifr);
+	err = ioctl(sk, SIOCSIFMTU, (caddr_t) &ifr);
 
-	close(sock);
-	return (rc < 0) ? FALSE : TRUE;
+	close(sk);
+
+	if (err < 0)
+		return FALSE;
+
+	return TRUE;
 }
 
 void ppp_net_process_packet(struct ppp_net *net, const guint8 *packet)
 {
-	GError *error = NULL;
 	GIOStatus status;
 	gsize bytes_written;
 	guint16 len;
@@ -84,7 +86,7 @@
 	/* find the length of the packet to transmit */
 	len = get_host_short(&packet[2]);
 	status = g_io_channel_write_chars(net->channel, (gchar *) packet,
-						len, &bytes_written, &error);
+						len, &bytes_written, NULL);
 }
 
 /*
@@ -97,7 +99,6 @@
 	struct ppp_net *net = (struct ppp_net *) userdata;
 	GIOStatus status;
 	gsize bytes_read;
-	GError *error = NULL;
 	gchar *buf = (gchar *) net->ppp_packet->info;
 
 	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
@@ -106,7 +107,7 @@
 	if (cond & G_IO_IN) {
 		/* leave space to add PPP protocol field */
 		status = g_io_channel_read_chars(channel, buf, net->mtu,
-							&bytes_read, &error);
+							&bytes_read, NULL);
 		if (bytes_read > 0)
 			ppp_transmit(net->ppp, (guint8 *) net->ppp_packet,
 					bytes_read);
@@ -125,10 +126,9 @@
 struct ppp_net *ppp_net_new(GAtPPP *ppp)
 {
 	struct ppp_net *net;
-	int fd;
-	struct ifreq ifr;
 	GIOChannel *channel = NULL;
-	int err;
+	struct ifreq ifr;
+	int fd, err;
 
 	net = g_try_new0(struct ppp_net, 1);
 	if (net == NULL)
@@ -149,7 +149,7 @@
 	ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
 	strcpy(ifr.ifr_name, "ppp%d");
 
-	err = ioctl(fd, TUNSETIFF, (void *)&ifr);
+	err = ioctl(fd, TUNSETIFF, (void *) &ifr);
 	if (err < 0)
 		goto error;
 
--- gdbus/mainloop.c
+++ gdbus/mainloop.c
@@ -95,6 +95,7 @@
 {
 	struct watch_info *info = data;
 	unsigned int flags = 0;
+	DBusDispatchStatus status;
 
 	dbus_connection_ref(info->conn);
 
@@ -105,6 +106,9 @@
 
 	dbus_watch_handle(info->watch, flags);
 
+	status = dbus_connection_get_dispatch_status(info->conn);
+	queue_dispatch(info->conn, status);
+
 	dbus_connection_unref(info->conn);
 
 	return TRUE;
--- gisi/client.c
+++ gisi/client.c
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009-2010  Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
@@ -64,6 +64,7 @@
 
 struct _GIsiClient {
 	uint8_t resource;
+	uint16_t server_obj;
 	struct {
 		int major;
 		int minor;
@@ -202,6 +203,31 @@
 }
 
 /**
+ * Set the server object for the resource associated with @a
+ * client.
+ * @param client client for the resource
+ * @param server object
+ */
+void g_isi_server_object_set(GIsiClient *client, uint16_t obj)
+{
+	if (!client)
+		return;
+
+	client->server_obj = obj;
+}
+
+/**
+ * Returns the server object for the the resource associated with @a
+ * client.
+ * @param client client for the resource
+ * @return server object
+ */
+uint8_t g_isi_server_object(GIsiClient *client)
+{
+	return client ? client->server_obj : 0;
+}
+
+/**
  * Returns the resource associated with @a client
  * @param client client for the resource
  * @return PhoNet resource ID for the client
@@ -260,43 +286,6 @@
 	g_free(ind);
 }
 
-static int g_isi_indication_init(GIsiClient *client)
-{
-	GIOChannel *channel;
-	uint8_t msg[] = {
-		0, PNS_SUBSCRIBED_RESOURCES_IND,
-		1, client->resource,
-	};
-
-	channel = phonet_new(client->modem, PN_COMMGR);
-	if (!channel)
-		return errno;
-
-	client->inds.fd = g_io_channel_unix_get_fd(channel);
-
-	/* Subscribe by sending an indication */
-	sendto(client->inds.fd, msg, 4, MSG_NOSIGNAL, (void *)&commgr,
-		sizeof(commgr));
-	client->inds.source = g_io_add_watch(channel,
-					G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
-					g_isi_callback, client);
-
-	g_io_channel_unref(channel);
-	return 0;
-}
-
-static void g_isi_indication_deinit(GIsiClient *client)
-{
-	uint8_t msg[] = {
-		0, PNS_SUBSCRIBED_RESOURCES_IND,
-		0,
-	};
-
-	/* Unsubscribe by sending an empty subscribe indication */
-	sendto(client->inds.fd, msg, 3, MSG_NOSIGNAL, (void *)&commgr,
-		sizeof(commgr));
-}
-
 /**
  * Destroys an ISI client, cancels all pending transactions and subscriptions.
  * @param client client to destroy (may be NULL)
@@ -307,15 +296,16 @@
 		return;
 
 	tdestroy(client->reqs.pending, g_isi_cleanup_req);
-	tdestroy(client->inds.subs, g_isi_cleanup_ind);
-
 	if (client->reqs.source > 0)
 		g_source_remove(client->reqs.source);
 
+	tdestroy(client->inds.subs, g_isi_cleanup_ind);
+	client->inds.subs = NULL;
+	client->inds.count = 0;
+	g_isi_commit_subscriptions(client);
 	if (client->inds.source > 0)
 		g_source_remove(client->inds.source);
 
-	g_isi_indication_deinit(client);
 	g_free(client);
 }
 
@@ -354,6 +344,42 @@
 }
 
 /**
+ * Send an ISI request to a specific Phonet address and register a callback
+ * to process the response(s) to the resulting transaction.
+ *
+ * @param client ISI client (from g_isi_client_create())
+ * @param dst Phonet destination address
+ * @param buf pointer to request payload
+ * @param len request payload byte length
+ * @param timeout timeout in seconds
+ * @param cb callback to process response(s)
+ * @param opaque data for the callback
+ * @param notify finalizer function for the @a opaque data (may be NULL)
+ *
+ * @return
+ * A pointer to a newly created GIsiRequest.
+ *
+ * @errors
+ * If an error occurs, @a errno is set accordingly and a NULL pointer is
+ * returned.
+ */
+GIsiRequest *g_isi_sendto(GIsiClient *client,
+				struct sockaddr_pn *dst,
+				const void *__restrict buf, size_t len,
+				unsigned timeout,
+				GIsiResponseFunc cb, void *opaque,
+				GDestroyNotify notify)
+{
+	const struct iovec iov = {
+		.iov_base = (void *)buf,
+		.iov_len = len,
+	};
+
+	return g_isi_vsendto(client, dst, &iov, 1, timeout, cb, opaque, notify);
+}
+
+
+/**
  * Send an ISI request and register a callback to process the response(s) to
  * the resulting transaction.
  *
@@ -388,10 +414,11 @@
 
 
 /**
- * Send an ISI request and register a callback to process the response(s) to
- * the resulting transaction.
+ * Send an ISI request to a specific Phonet address and register a callback
+ * to process the response(s) to the resulting transaction.
  *
- * @param cl ISI client (from g_isi_client_create())
+ * @param client ISI client (from g_isi_client_create())
+ * @param dst Phonet destination address
  * @param iov scatter-gather array to the request payload
  * @param iovlen number of vectors in the scatter-gather array
  * @param timeout timeout in seconds
@@ -406,19 +433,17 @@
  * If an error occurs, @a errno is set accordingly and a NULL pointer is
  * returned.
  */
-GIsiRequest *g_isi_vsend(GIsiClient *client,
+GIsiRequest *g_isi_vsendto(GIsiClient *client,
+				struct sockaddr_pn *dst,
 				const struct iovec *__restrict iov,
 				size_t iovlen, unsigned timeout,
 				GIsiResponseFunc cb, void *opaque,
 				GDestroyNotify notify)
 {
 	struct iovec _iov[1 + iovlen];
-	struct sockaddr_pn dst = {
-		.spn_family = AF_PHONET,
-	};
 	struct msghdr msg = {
-		.msg_name = (void *)&dst,
-		.msg_namelen = sizeof(dst),
+		.msg_name = (void *)dst,
+		.msg_namelen = sizeof(*dst),
 		.msg_iov = _iov,
 		.msg_iovlen = 1 + iovlen,
 		.msg_control = NULL,
@@ -427,9 +452,10 @@
 	};
 	ssize_t ret;
 	size_t i, len;
+	unsigned int key;
 	uint8_t id;
 
-	GIsiRequest *req;
+	GIsiRequest *req = NULL;
 	GIsiRequest **old;
 
 	if (!client) {
@@ -437,25 +463,33 @@
 		return NULL;
 	}
 
-	req = g_try_new0(GIsiRequest, 1);
-	if (!req) {
-		errno = ENOMEM;
-		return NULL;
-	}
+	key = 1 + ((client->reqs.last + 1) % 255);
 
-	req->client = client;
-	req->id = (client->reqs.last + 1) % 255;
-	req->func = cb;
-	req->data = opaque;
-	req->notify = notify;
+	if (cb) {
+		req = g_try_new0(GIsiRequest, 1);
+		if (!req) {
+			errno = ENOMEM;
+			return NULL;
+		}
 
-	old = tsearch(req, &client->reqs.pending, g_isi_cmp);
-	if (!old) {
-		errno = ENOMEM;
-		goto error;
-	}
+		req->client = client;
+		req->id = key;
+		req->func = cb;
+		req->data = opaque;
+		req->notify = notify;
+
+		old = tsearch(req, &client->reqs.pending, g_isi_cmp);
+		if (!old) {
+			errno = ENOMEM;
+			goto error;
+		}
+		if (*old == req)
+			old = NULL;
 
-	if (*old != req) {
+	} else
+		old = tfind(&key, &client->reqs.pending, g_isi_cmp);
+
+	if (old) {
 		/* FIXME: perhaps retry with randomized access after
 		 * initial miss. Although if the rate at which
 		 * requests are sent is so high that the transaction
@@ -465,9 +499,7 @@
 		goto error;
 	}
 
-	dst.spn_resource = client->resource,
-
-	id = req->id;
+	id = key;
 	_iov[0].iov_base = &id;
 	_iov[0].iov_len = 1;
 
@@ -489,8 +521,10 @@
 		goto error;
 	}
 
-	req->timeout = g_timeout_add_seconds(timeout, g_isi_timeout, req);
-	client->reqs.last = req->id;
+	if (req && timeout)
+		req->timeout = g_timeout_add_seconds(timeout, g_isi_timeout,
+							req);
+	client->reqs.last = key;
 	return req;
 
 error:
@@ -501,6 +535,46 @@
 }
 
 /**
+ * Send an ISI request and register a callback to process the response(s) to
+ * the resulting transaction.
+ *
+ * @param cl ISI client (from g_isi_client_create())
+ * @param iov scatter-gather array to the request payload
+ * @param iovlen number of vectors in the scatter-gather array
+ * @param timeout timeout in seconds
+ * @param cb callback to process response(s)
+ * @param opaque data for the callback
+ * @param notify finalizer function for the @a opaque data (may be NULL)
+ *
+ * @return
+ * A pointer to a newly created GIsiRequest.
+ *
+ * @errors
+ * If an error occurs, @a errno is set accordingly and a NULL pointer is
+ * returned.
+ */
+GIsiRequest *g_isi_vsend(GIsiClient *client,
+				const struct iovec *__restrict iov,
+				size_t iovlen, unsigned timeout,
+				GIsiResponseFunc cb, void *opaque,
+				GDestroyNotify notify)
+{
+	struct sockaddr_pn dst = {
+		.spn_family = AF_PHONET,
+	};
+
+	if (!client) {
+		errno = EINVAL;
+		return NULL;
+	}
+
+	dst.spn_resource = client->resource;
+
+	return g_isi_vsendto(client, &dst, iov, iovlen, timeout,
+				cb, opaque, notify);
+}
+
+/**
  * Cancels a pending request, i.e. stop waiting for responses and cancels the
  * timeout.
  * @param req request to cancel
@@ -521,31 +595,96 @@
 	g_free(req);
 }
 
+static uint8_t *__msg;
+static void build_subscribe_msg(const void *nodep,
+				const VISIT which,
+				const int depth)
+{
+	GIsiIndication *ind = *(GIsiIndication **)nodep;
+	uint8_t res = ind->type >> 8;
+
+	switch (which) {
+	case postorder:
+	case leaf:
+		if (__msg[2] && res == __msg[2+__msg[2]])
+			break;
+		__msg[2]++;
+		__msg[2+__msg[2]] = res;
+		break;
+	default:
+		break;
+	}
+}
+
 /**
- * Subscribe to a given indication type for the resource that an ISI client
- * is associated with. If the same type was already subscribed, the old
- * subscription is overriden.
- * @param cl ISI client (from g_isi_client_create())
+ * Subscribe indications from the modem.
+ * @param client ISI client (from g_isi_client_create())
+ * @return 0 on success, a system error code otherwise.
+ */
+int g_isi_commit_subscriptions(GIsiClient *client)
+{
+	GIOChannel *channel;
+	uint8_t msg[3+256] = {
+		0, PNS_SUBSCRIBED_RESOURCES_IND,
+		0,
+	};
+
+	if (!client)
+		return -EINVAL;
+
+	if (!client->inds.source) {
+		if (client->inds.count == 0)
+			return 0;
+
+		channel = phonet_new(client->modem, PN_COMMGR);
+		if (!channel)
+			return -errno;
+
+		client->inds.fd = g_io_channel_unix_get_fd(channel);
+
+		client->inds.source = g_io_add_watch(channel,
+						G_IO_IN|G_IO_ERR|
+						G_IO_HUP|G_IO_NVAL,
+						g_isi_callback, client);
+
+		g_io_channel_unref(channel);
+	}
+
+	__msg = msg;
+	twalk(client->inds.subs, build_subscribe_msg);
+
+	/* Subscribe by sending an indication */
+	sendto(client->inds.fd, msg, 3+msg[2], MSG_NOSIGNAL, (void *)&commgr,
+		sizeof(commgr));
+	return 0;
+}
+
+/**
+ * Add subscription for a given indication type from the given resource.
+ * If the same type was already subscribed, the old subscription
+ * is overriden. Subscriptions for newly added resources do not become
+ * effective until g_isi_commit_subscriptions() has been called.
+ * @param client ISI client (from g_isi_client_create())
+ * @param res resource id
  * @param type indication type
  * @param cb callback to process received indications
  * @param data data for the callback
  * @return 0 on success, a system error code otherwise.
  */
-int g_isi_subscribe(GIsiClient *client, uint8_t type,
-			GIsiIndicationFunc cb, void *data)
+int g_isi_add_subscription(GIsiClient *client, uint8_t res, uint8_t type,
+				GIsiIndicationFunc cb, void *data)
 {
 	GIsiIndication *ind;
 	GIsiIndication **old;
-	gboolean isnew = TRUE;
 
-	if (cb == NULL)
+	if (client == NULL || cb == NULL)
 		return -EINVAL;
 
 	ind = g_try_new0(GIsiIndication, 1);
 	if (!ind)
 		return -ENOMEM;
 
-	ind->type = type;
+	ind->type = (res << 8) | type;
 
 	old = tsearch(ind, &client->inds.subs, g_isi_cmp);
 	if (!old) {
@@ -559,36 +698,54 @@
 	if (*old != ind) {
 		g_free(ind);
 		ind = *old;
-		isnew = FALSE;
-	}
+	} else
+		client->inds.count++;
 
 	ind->func = cb;
 	ind->data = data;
 
-	if (client->inds.count == 0) {
-		int ret = g_isi_indication_init(client);
-		if (ret) {
-			tdelete(ind, &client->inds.subs, g_isi_cmp);
-			g_free(ind);
-			return ret;
-		}
-	}
+	return 0;
+}
 
-	if (isnew)
-		client->inds.count++;
+/**
+ * Subscribe to a given indication type for the resource that an ISI client
+ * is associated with. If the same type was already subscribed, the old
+ * subscription is overriden. For multiple subscriptions,
+ * g_isi_add_subcription() and g_isi_commit_subscriptions() should be used
+ * instead.
+ * @param cl ISI client (from g_isi_client_create())
+ * @param type indication type
+ * @param cb callback to process received indications
+ * @param data data for the callback
+ * @return 0 on success, a system error code otherwise.
+ */
+int g_isi_subscribe(GIsiClient *client, uint8_t type,
+			GIsiIndicationFunc cb, void *data)
+{
+	int ret;
 
-	return 0;
+	if (!client)
+		return -EINVAL;
+
+	ret = g_isi_add_subscription(client, client->resource, type, cb, data);
+	if (ret)
+		return ret;
+
+	return g_isi_commit_subscriptions(client);
 }
 
 /**
- * Unsubscribe from a given indication type.
+ * Remove subscription for a given indication type from the given resource.
+ * g_isi_commit_subcsriptions() should be called after modifications to
+ * cancel unnecessary resource subscriptions from the modem.
  * @param client ISI client (from g_isi_client_create())
- * @param type indication type.
+ * @param res resource id
+ * @param type indication type
  */
-void g_isi_unsubscribe(GIsiClient *client, uint8_t type)
+void g_isi_remove_subscription(GIsiClient *client, uint8_t res, uint8_t type)
 {
 	GIsiIndication *ind;
-	unsigned int id = type;
+	unsigned int id = (res << 8) | type;
 
 	if (!client)
 		return;
@@ -597,18 +754,33 @@
 	if (!ind)
 		return;
 
-	if (--client->inds.count == 0)
-		g_isi_indication_deinit(client);
-
+	client->inds.count--;
 	g_free(ind);
 }
 
-static void g_isi_dispatch_indication(GIsiClient *client, uint16_t obj,
-					uint8_t *msg, size_t len)
+/**
+ * Unsubscribe from a given indication type. For removing multiple
+ * subscriptions, g_isi_remove_subcription() and
+ * g_isi_commit_subscriptions() should be used instead.
+ * @param client ISI client (from g_isi_client_create())
+ * @param type indication type.
+ */
+void g_isi_unsubscribe(GIsiClient *client, uint8_t type)
+{
+	if (!client)
+		return;
+
+	g_isi_remove_subscription(client, client->resource, type);
+	g_isi_commit_subscriptions(client);
+}
+
+static void g_isi_dispatch_indication(GIsiClient *client, uint8_t res,
+					uint16_t obj, uint8_t *msg,
+					size_t len)
 {
 	void *ret;
 	GIsiIndication *ind;
-	unsigned type = msg[0];
+	unsigned type = (res << 8) | msg[0];
 
 	ret = tfind(&type, &client->inds.subs, g_isi_cmp);
 	if (!ret)
@@ -620,8 +792,9 @@
 		ind->func(client, msg, len, obj, ind->data);
 }
 
-static void g_isi_dispatch_response(GIsiClient *client, uint16_t obj,
-					uint8_t *msg, size_t len)
+static void g_isi_dispatch_response(GIsiClient *client, uint8_t res,
+					uint16_t obj, uint8_t *msg,
+					size_t len)
 {
 	void *ret;
 	GIsiRequest *req;
@@ -632,7 +805,7 @@
 		/* This could either be an unsolicited response, which
 		 * we will ignore, or an incoming request, which we
 		 * handle just like an incoming indication */
-		g_isi_dispatch_indication(client, obj, msg + 1, len - 1);
+		g_isi_dispatch_indication(client, res, obj, msg + 1, len - 1);
 		return;
 	}
 
@@ -664,7 +837,7 @@
 		uint8_t res;
 
 		len = phonet_read(channel, buf, len, &obj, &res);
-		if (len < 2 || res != client->resource)
+		if (len < 2)
 			return TRUE;
 
 		msg = (uint8_t *)buf;
@@ -674,11 +847,11 @@
 						client->debug_data);
 
 		if (fd == client->reqs.fd)
-			g_isi_dispatch_response(client, obj, msg, len);
+			g_isi_dispatch_response(client, res, obj, msg, len);
 		else
 			/* Transaction field at first byte is
 			 * discarded with indications */
-			g_isi_dispatch_indication(client, obj, msg + 1,
+			g_isi_dispatch_indication(client, res, obj, msg + 1,
 							len - 1);
 	}
 	return TRUE;
--- gisi/client.h
+++ gisi/client.h
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
@@ -29,6 +29,7 @@
 #include <stdint.h>
 #include <glib/gtypes.h>
 #include <gisi/modem.h>
+#include "phonet.h"
 
 struct _GIsiClient;
 typedef struct _GIsiClient GIsiClient;
@@ -52,12 +53,18 @@
 GIsiRequest *g_isi_verify(GIsiClient *client, GIsiVerifyFunc func,
 				void *opaque);
 
+GIsiRequest *g_isi_verify_resource(GIsiClient *client, uint8_t resource,
+					GIsiVerifyFunc func, void *opaque);
+
 uint8_t g_isi_client_resource(GIsiClient *client);
 
 void g_isi_version_set(GIsiClient *client, int major, int minor);
 int g_isi_version_major(GIsiClient *client);
 int g_isi_version_minor(GIsiClient *client);
 
+void g_isi_server_object_set(GIsiClient *client, uint16_t obj);
+uint8_t g_isi_server_object(GIsiClient *client);
+
 void g_isi_client_set_debug(GIsiClient *client, GIsiDebugFunc func,
 				void *opaque);
 
@@ -73,11 +80,25 @@
 					size_t iovlen, unsigned timeout,
 					GIsiResponseFunc func, void *opaque);
 
+GIsiRequest *g_isi_sendto(GIsiClient *client,
+				struct sockaddr_pn *dst,
+				const void *data, size_t len,
+				unsigned timeout,
+				GIsiResponseFunc func, void *opaque,
+				GDestroyNotify notify);
+
 GIsiRequest *g_isi_send(GIsiClient *client, const void *data, size_t len,
 			unsigned timeout,
 			GIsiResponseFunc func, void *opaque,
 			GDestroyNotify notify);
 
+GIsiRequest *g_isi_vsendto(GIsiClient *client,
+				struct sockaddr_pn *dst,
+				const struct iovec *iov, size_t iovlen,
+				unsigned timeout,
+				GIsiResponseFunc func, void *opaque,
+				GDestroyNotify notify);
+
 GIsiRequest *g_isi_vsend(GIsiClient *client,
 				const struct iovec *iov, size_t iovlen,
 				unsigned timeout,
@@ -86,9 +107,13 @@
 
 void g_isi_request_cancel(GIsiRequest *req);
 
+int g_isi_commit_subscriptions(GIsiClient *client);
+int g_isi_add_subscription(GIsiClient *client, uint8_t res, uint8_t type,
+				GIsiIndicationFunc cb, void *data);
+void g_isi_remove_subscription(GIsiClient *client, uint8_t res, uint8_t type);
+
 int g_isi_subscribe(GIsiClient *client, uint8_t type,
 			GIsiIndicationFunc func, void *opaque);
-
 void g_isi_unsubscribe(GIsiClient *client, uint8_t type);
 
 #ifdef __cplusplus
--- gisi/iter.c
+++ gisi/iter.c
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Rémi Denis-Courmont <remi.denis-courmont at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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
+ *  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
  *
  */
 
--- gisi/iter.h
+++ gisi/iter.h
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Rémi Denis-Courmont <remi.denis-courmont at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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
+ *  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
  *
  */
 
--- gisi/modem.c
+++ gisi/modem.c
@@ -1,19 +1,22 @@
-/**
- * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
- * 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
--- gisi/modem.h
+++ gisi/modem.h
@@ -1,21 +1,22 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
- * 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
  */
 
 #ifndef __GISI_MODEM_H
--- gisi/netlink.c
+++ gisi/netlink.c
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Rémi Denis-Courmont <remi.denis-courmont at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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
+ *  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
  *
  */
 
--- gisi/netlink.h
+++ gisi/netlink.h
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Rémi Denis-Courmont <remi.denis-courmont at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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
+ *  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
  *
  */
 
--- gisi/pep.c
+++ gisi/pep.c
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Rémi Denis-Courmont <remi.denis-courmont at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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
+ *  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
  *
  */
 
--- gisi/pep.h
+++ gisi/pep.h
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Rémi Denis-Courmont <remi.denis-courmont at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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
+ *  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
  *
  */
 
--- gisi/phonet.h
+++ gisi/phonet.h
@@ -1,22 +1,24 @@
-/**
- * Phonet sockets kernel interface
+/*
  *
- * Copyright (C) 2008 Nokia Corporation. All rights reserved.
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
- * 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
  */
+
 #ifndef NETPHONET_PHONET_H
 #define NETPHONET_PHONET_H
 
--- gisi/pipe.c
+++ gisi/pipe.c
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Rémi Denis-Courmont <remi.denis-courmont at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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
+ *  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
  *
  */
 
--- gisi/pipe.h
+++ gisi/pipe.h
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Rémi Denis-Courmont <remi.denis-courmont at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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
+ *  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
  *
  */
 
--- gisi/server.c
+++ gisi/server.c
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- gisi/server.h
+++ gisi/server.h
@@ -1,21 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *  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
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ *  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
  *
  */
 
--- gisi/socket.c
+++ gisi/socket.c
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Rémi Denis-Courmont <remi.denis-courmont at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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
+ *  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
  *
  */
 
--- gisi/socket.h
+++ gisi/socket.h
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Rémi Denis-Courmont <remi.denis-courmont at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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
+ *  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
  *
  */
 
--- gisi/verify.c
+++ gisi/verify.c
@@ -1,23 +1,21 @@
 /*
- * This file is part of oFono - Open Source Telephony
  *
- * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *  oFono - Open Source Telephony
  *
- * Contact: Rémi Denis-Courmont <remi.denis-courmont at nokia.com>
+ *  Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
  *
- * 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
+ *  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
  *
  */
 
@@ -42,19 +40,27 @@
 	GIsiVerifyFunc func;
 	void *data;
 	guint count;
+	uint8_t resource;
 };
 
 static GIsiRequest *send_version_query(GIsiClient *client, GIsiResponseFunc cb,
 					void *opaque)
 {
+	struct verify_data *vd = opaque;
+
+	struct sockaddr_pn dst = {
+		.spn_family = AF_PHONET,
+		.spn_resource = vd->resource,
+	};
+
 	uint8_t msg[] = {
 		COMMON_MESSAGE,
 		COMM_ISI_VERSION_GET_REQ,
 		0x00  /* Filler */
 	};
 
-	return g_isi_request_make(client, msg, sizeof(msg), VERSION_TIMEOUT,
-					cb, opaque);
+	return g_isi_sendto(client, &dst, msg, sizeof(msg), VERSION_TIMEOUT,
+			cb, opaque, NULL);
 }
 
 static gboolean verify_cb(GIsiClient *client, const void *restrict data,
@@ -85,7 +91,10 @@
 		goto out;
 
 	if (msg[1] == COMM_ISI_VERSION_GET_RESP && len >= 4) {
-		g_isi_version_set(client, msg[2], msg[3]);
+		if (vd->resource == g_isi_client_resource(client)) {
+			g_isi_version_set(client, msg[2], msg[3]);
+			g_isi_server_object_set(client, object);
+		}
 		alive = TRUE;
 		goto out;
 	}
@@ -103,8 +112,10 @@
 
 /**
  * Verifies reachability of @a client with its resource. As a side
- * effect of this liveliness check, the ISI version of the client
- * resource will be made available via g_isi_client_version().
+ * effect of this liveliness check, the ISI version of the interface
+ * and the server object implementing the resource will be made
+ * available via g_isi_client_version() and g_isi_server_object(),
+ * respectively.
  * @param client client to verify
  * @param func callback to process outcome
  * @param opaque user data
@@ -116,8 +127,36 @@
 	struct verify_data *data = g_try_new0(struct verify_data, 1);
 	GIsiRequest *req = NULL;
 
+	if (data == NULL)
+		return NULL;
+
+	data->func = func;
+	data->data = opaque;
+	data->resource = g_isi_client_resource(client);
+
+	req = send_version_query(client, verify_cb, data);
+	if (!req)
+		g_free(data);
+
+	return req;
+}
+
+/**
+ * Verifies the reachability of an arbitrary resource.
+ * @param client client to verify
+ * @param func callback to process outcome
+ * @param opaque user data
+ * @return NULL on error (see errno), GIsiRequest pointer on success.
+ */
+GIsiRequest *g_isi_verify_resource(GIsiClient *client, uint8_t resource,
+				GIsiVerifyFunc func, void *opaque)
+{
+	struct verify_data *data = g_try_new0(struct verify_data, 1);
+	GIsiRequest *req = NULL;
+
 	data->func = func;
 	data->data = opaque;
+	data->resource = resource;
 
 	req = send_version_query(client, verify_cb, data);
 	if (!req)
--- include/sim.h
+++ include/sim.h
@@ -82,6 +82,7 @@
 					enum ofono_sim_file_structure structure,
 					int recordlength,
 					const unsigned char access[3],
+					unsigned char file_status,
 					void *data);
 
 typedef void (*ofono_sim_read_cb_t)(const struct ofono_error *error,
--- include/stk.h
+++ include/stk.h
@@ -67,6 +67,13 @@
 
 void ofono_stk_proactive_session_end_notify(struct ofono_stk *stk);
 
+void ofono_stk_proactive_command_handled_notify(struct ofono_stk *stk,
+						int length,
+						const unsigned char *pdu);
+void ofono_stk_terminal_response_sent_notify(struct ofono_stk *stk,
+						int length,
+						const unsigned char *pdu);
+
 #ifdef __cplusplus
 }
 #endif
--- include/voicecall.h
+++ include/voicecall.h
@@ -65,31 +65,74 @@
 			const struct ofono_phone_number *number,
 			enum ofono_clir_option clir, enum ofono_cug_option cug,
 			ofono_voicecall_cb_t cb, void *data);
+	/* Answers an incoming call, this usually corresponds to ATA */
 	void (*answer)(struct ofono_voicecall *vc,
 			ofono_voicecall_cb_t cb, void *data);
+
+	/* Hangs up active, dialing, alerting or incoming calls */
 	void (*hangup_active)(struct ofono_voicecall *vc,
 			ofono_voicecall_cb_t cb, void *data);
+	/* Hangs up all calls except waiting calls */
 	void (*hangup_all)(struct ofono_voicecall *vc,
 			ofono_voicecall_cb_t cb, void *data);
+	/*
+	 * Holds all active and retrieves held or waiting calls, this usually
+	 * corresponds to +CHLD=2
+	 */
 	void (*hold_all_active)(struct ofono_voicecall *vc,
 			ofono_voicecall_cb_t cb, void *data);
+	/* Releases all held calls, this usually corresponds to +CHLD=0*/
 	void (*release_all_held)(struct ofono_voicecall *vc,
 			ofono_voicecall_cb_t cb, void *data);
+	/*
+	 * Sets the UDUB condition on a waiting call.  This usually
+	 * corresponds to +CHLD=0
+	 */
 	void (*set_udub)(struct ofono_voicecall *vc,
 			ofono_voicecall_cb_t cb, void *data);
+	/*
+	 * Releases all active calls and accepts a possible waiting call.
+	 * This usually corresponds to +CHLD=1
+	 */
 	void (*release_all_active)(struct ofono_voicecall *vc,
 			ofono_voicecall_cb_t cb, void *data);
+	/*
+	 * Releases a specific call given by id.  This usually corresponds to
+	 * +CHLD=1X.  In 3GPP this command is only guaranteed to affect active
+	 * calls.  Plugins are encouraged to implement this using vendor
+	 * commands that can also affect held calls
+	 */
 	void (*release_specific)(struct ofono_voicecall *vc, int id,
 			ofono_voicecall_cb_t cb, void *data);
+	/*
+	 * Breaks out a party given by id from a multiparty call.  This
+	 * usually corresponds to +CHLD=2X
+	 */
 	void (*private_chat)(struct ofono_voicecall *vc, int id,
 			ofono_voicecall_cb_t cb, void *data);
+	/*
+	 * Joins held and active calls together into a multiparty call.  This
+	 * usually corresponds to +CHLD=3
+	 */
 	void (*create_multiparty)(struct ofono_voicecall *vc,
 			ofono_voicecall_cb_t cb, void *data);
+	/*
+	 * Connects two calls together and disconnects from both calls.  This
+	 * usually corresponds to +CHLD=4
+	 */
 	void (*transfer)(struct ofono_voicecall *vc,
 			ofono_voicecall_cb_t cb, void *data);
+	/*
+	 * Deflects an incoming or waiting call to a given number.  This
+	 * usually corresponds to +CTFR
+	 */
 	void (*deflect)(struct ofono_voicecall *vc,
 			const struct ofono_phone_number *ph,
 			ofono_voicecall_cb_t cb, void *data);
+	/*
+	 * This is equivalent to +CHLD=2 but does not affect a possible
+	 * waiting call.
+	 */
 	void (*swap_without_accept)(struct ofono_voicecall *vc,
 			ofono_voicecall_cb_t cb, void *data);
 	void (*send_tones)(struct ofono_voicecall *vc, const char *tones,
--- plugins/huawei.c
+++ plugins/huawei.c
@@ -77,6 +77,7 @@
 	struct ofono_gprs *gprs;
 	struct ofono_gprs_context *gc;
 	gboolean voice;
+	gboolean ndis;
 	guint sim_poll_timeout;
 	guint sim_poll_count;
 };
@@ -108,7 +109,9 @@
 
 	ofono_modem_set_data(modem, NULL);
 
-	g_at_chat_unref(data->modem);
+	if (data->modem)
+		g_at_chat_unref(data->modem);
+
 	g_at_chat_unref(data->pcui);
 	g_free(data);
 }
@@ -465,12 +468,15 @@
 
 	DBG("%p", modem);
 
-	data->modem = open_device(modem, "Modem", "Modem: ");
-	if (data->modem == NULL)
-		return -EINVAL;
+	if (ofono_modem_get_string(modem, "NDIS") == NULL) {
+		data->modem = open_device(modem, "Modem", "Modem: ");
+		if (data->modem == NULL)
+			return -EINVAL;
 
-	g_at_chat_set_disconnect_function(data->modem,
+		g_at_chat_set_disconnect_function(data->modem,
 						huawei_disconnect, modem);
+	} else
+		data->ndis = TRUE;
 
 	data->pcui = open_device(modem, "Pcui", "PCUI: ");
 	if (data->pcui == NULL) {
@@ -484,7 +490,8 @@
 
 	data->sim_state = 0;
 
-	g_at_chat_send(data->pcui, "ATE0", none_prefix, NULL, NULL, NULL);
+	g_at_chat_send(data->pcui, "ATE0 +CMEE=1", none_prefix,
+						NULL, NULL, NULL);
 
 	g_at_chat_send(data->pcui, "AT+CFUN=1", none_prefix,
 					cfun_enable, modem, NULL);
@@ -606,8 +613,8 @@
 	struct ofono_message_waiting *mw;
 
 	if (data->sim_state != HUAWEI_SIM_STATE_VALID &&
-	    data->sim_state != HUAWEI_SIM_STATE_INVALID_CS &&
-	    data->sim_state != HUAWEI_SIM_STATE_INVALID_PS) {
+			data->sim_state != HUAWEI_SIM_STATE_INVALID_CS &&
+			data->sim_state != HUAWEI_SIM_STATE_INVALID_PS) {
 		ofono_info("huawei: invalid sim state in post online (%d)",
 				data->sim_state);
 		return;
@@ -625,8 +632,12 @@
 	if (data->sim_state == HUAWEI_SIM_STATE_VALID ||
 			data->sim_state == HUAWEI_SIM_STATE_INVALID_CS) {
 		data->gprs = ofono_gprs_create(modem, 0, "atmodem", data->pcui);
-		data->gc = ofono_gprs_context_create(modem, 0, "atmodem",
-								data->modem);
+		if (data->ndis == TRUE)
+			data->gc = ofono_gprs_context_create(modem, 0,
+						"huaweimodem", data->pcui);
+		else
+			data->gc = ofono_gprs_context_create(modem, 0,
+						"atmodem", data->modem);
 
 		if (data->gprs && data->gc)
 			ofono_gprs_add_context(data->gprs, data->gc);
--- plugins/ifx.c
+++ plugins/ifx.c
@@ -62,17 +62,19 @@
 #include <drivers/atmodem/atutil.h>
 #include <drivers/atmodem/vendor.h>
 
-#define NUM_DLC  4
+#define NUM_DLC  5
 
 #define VOICE_DLC   0
 #define NETREG_DLC  1
-#define GPRS_DLC    2
-#define AUX_DLC     3
+#define GPRS1_DLC   2
+#define GPRS2_DLC   3
+#define AUX_DLC     4
 
-static char *dlc_prefixes[NUM_DLC] = { "Voice: ", "Net: ", "GPRS: ", "Aux: " };
+static char *dlc_prefixes[NUM_DLC] = { "Voice: ", "Net: ",
+					"GPRS1: ", "GPRS2: ", "Aux: " };
 
 static const char *dlc_nodes[NUM_DLC] = { "/dev/ttyGSM1", "/dev/ttyGSM2",
-					"/dev/ttyGSM7", "/dev/ttyGSM8" };
+			"/dev/ttyGSM3", "/dev/ttyGSM4", "/dev/ttyGSM6" };
 
 static const char *none_prefix[] = { NULL };
 static const char *xdrv_prefix[] = { "+XDRV:", NULL };
@@ -83,6 +85,7 @@
 	GAtChat *dlcs[NUM_DLC];
 	guint dlc_poll_count;
 	guint dlc_poll_source;
+	guint dlc_init_source;
 	guint frame_size;
 	int mux_ldisc;
 	int saved_ldisc;
@@ -198,9 +201,6 @@
 	if (getenv("OFONO_AT_DEBUG"))
 		g_at_chat_set_debug(chat, ifx_debug, debug);
 
-	g_at_chat_send(chat, "ATE0 +CMEE=1", NULL,
-					NULL, NULL, NULL);
-
 	return chat;
 }
 
@@ -210,6 +210,11 @@
 
 	DBG("");
 
+	if (data->dlc_init_source > 0) {
+		g_source_remove(data->dlc_init_source);
+		data->dlc_init_source = 0;
+	}
+
 	for (i = 0; i < NUM_DLC; i++) {
 		if (!data->dlcs[i])
 			continue;
@@ -264,6 +269,10 @@
 		data->audio_context = 0;
 	}
 
+	/* disable UART for power saving */
+	g_at_chat_send(data->dlcs[AUX_DLC], "AT+XPOW=0,0,0", none_prefix,
+							NULL, NULL, NULL);
+
 	if (data->audio_setting && data->audio_source && data->audio_dest) {
 		char buf[64];
 
@@ -334,6 +343,29 @@
 					xgendata_query, modem, NULL);
 }
 
+static gboolean dlc_setup(gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct ifx_data *data = ofono_modem_get_data(modem);
+	int i;
+
+	DBG("");
+
+	for (i = 0; i < NUM_DLC; i++)
+		g_at_chat_send(data->dlcs[i], "ATE0 +CMEE=1", NULL,
+						NULL, NULL, NULL);
+
+	g_at_chat_set_slave(data->dlcs[GPRS1_DLC], data->dlcs[NETREG_DLC]);
+	g_at_chat_set_slave(data->dlcs[GPRS2_DLC], data->dlcs[NETREG_DLC]);
+
+	g_at_chat_send(data->dlcs[AUX_DLC], "AT+CFUN=4", NULL,
+					cfun_enable, modem, NULL);
+
+	data->dlc_init_source = 0;
+
+	return FALSE;
+}
+
 static gboolean dlc_ready_check(gpointer user_data)
 {
 	struct ofono_modem *modem = user_data;
@@ -363,11 +395,11 @@
 		}
 	}
 
-	g_at_chat_send(data->dlcs[AUX_DLC], "AT+CFUN=4", NULL,
-					cfun_enable, modem, NULL);
-
 	data->dlc_poll_source = 0;
 
+	/* iterate through mainloop */
+	data->dlc_init_source = g_timeout_add_seconds(0, dlc_setup, modem);
+
 	return FALSE;
 
 error:
@@ -397,7 +429,7 @@
 	if (!data->mux)
 		goto error;
 
-	if (getenv("OFONO_AT_DEBUG"))
+	if (getenv("OFONO_MUX_DEBUG"))
 		g_at_mux_set_debug(data->mux, ifx_debug, "MUX: ");
 
 	g_at_mux_start(data->mux);
@@ -412,8 +444,8 @@
 		}
         }
 
-	g_at_chat_send(data->dlcs[AUX_DLC], "AT+CFUN=4", NULL,
-					cfun_enable, modem, NULL);
+	/* wait for DLC creation to settle */
+	data->dlc_init_source = g_timeout_add(10, dlc_setup, modem);
 
 	return;
 
@@ -622,7 +654,8 @@
 	DBG("%p", modem);
 
 	ofono_stk_create(modem, 0, "ifxmodem", data->dlcs[AUX_DLC]);
-	ofono_phonebook_create(modem, 0, "atmodem", data->dlcs[AUX_DLC]);
+	ofono_phonebook_create(modem, OFONO_VENDOR_IFX,
+					"atmodem", data->dlcs[AUX_DLC]);
 }
 
 static void ifx_post_online(struct ofono_modem *modem)
@@ -630,7 +663,7 @@
 	struct ifx_data *data = ofono_modem_get_data(modem);
 	struct ofono_message_waiting *mw;
 	struct ofono_gprs *gprs;
-	struct ofono_gprs_context *gc;
+	struct ofono_gprs_context *gc1, *gc2;
 
 	DBG("%p", modem);
 
@@ -655,11 +688,25 @@
 		ofono_message_waiting_register(mw);
 
 	gprs = ofono_gprs_create(modem, 0, "atmodem", data->dlcs[NETREG_DLC]);
-	gc = ofono_gprs_context_create(modem, 0,
-					"atmodem", data->dlcs[GPRS_DLC]);
+	if (!gprs)
+		return;
+
+	if (data->mux_ldisc < 0) {
+		gc1 = ofono_gprs_context_create(modem, 0,
+					"atmodem", data->dlcs[GPRS1_DLC]);
+		gc2 = ofono_gprs_context_create(modem, 0,
+					"atmodem", data->dlcs[GPRS2_DLC]);
+	} else {
+		gc1 = ofono_gprs_context_create(modem, 0,
+					"ifxmodem", data->dlcs[GPRS1_DLC]);
+		gc2 = ofono_gprs_context_create(modem, 0,
+					"ifxmodem", data->dlcs[GPRS2_DLC]);
+	}
 
-	if (gprs && gc)
-		ofono_gprs_add_context(gprs, gc);
+	if (gc1)
+		ofono_gprs_add_context(gprs, gc1);
+	if (gc2)
+		ofono_gprs_add_context(gprs, gc2);
 }
 
 static struct ofono_modem_driver ifx_driver = {
--- plugins/isigen.c
+++ plugins/isigen.c
@@ -428,6 +428,16 @@
 		DBG("Failed to add context");
 }
 
+static int isigen_enable(struct ofono_modem *modem)
+{
+	return 0;
+}
+
+static int isigen_disable(struct ofono_modem *modem)
+{
+	return 0;
+}
+
 static struct ofono_modem_driver driver = {
 	.name = "isigen",
 	.probe = isigen_probe,
@@ -436,6 +446,8 @@
 	.pre_sim = isigen_pre_sim,
 	.post_sim = isigen_post_sim,
 	.post_online = isigen_post_online,
+	.enable = isigen_enable,
+	.disable = isigen_disable,
 };
 
 static int isigen_init(void)
--- plugins/ofono.rules
+++ plugins/ofono.rules
@@ -54,6 +54,7 @@
 ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="140b", ENV{OFONO_IFACE_NUM}=="02", ENV{OFONO_HUAWEI_TYPE}="Pcui"
 
 ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="140c", ENV{OFONO_IFACE_NUM}=="00", ENV{OFONO_HUAWEI_TYPE}="Modem"
+ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="140c", ENV{OFONO_IFACE_NUM}=="01", ENV{OFONO_HUAWEI_TYPE}="NDIS"
 ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="140c", ENV{OFONO_IFACE_NUM}=="03", ENV{OFONO_HUAWEI_TYPE}="Pcui"
 
 ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="140d", ENV{OFONO_IFACE_NUM}=="00", ENV{OFONO_HUAWEI_TYPE}="Modem"
@@ -379,6 +380,10 @@
 ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1905", ENV{OFONO_DRIVER}="mbm"
 ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1906", ENV{OFONO_DRIVER}="mbm"
 
+# Ericsson c3607w
+ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190b", ENV{OFONO_DRIVER}="mbm"
+ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1049", ENV{OFONO_DRIVER}="mbm"
+
 # Ericsson F3307
 ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190a", ENV{OFONO_DRIVER}="mbm"
 ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1909", ENV{OFONO_DRIVER}="mbm"
@@ -397,6 +402,18 @@
 ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818b", ENV{OFONO_DRIVER}="mbm"
 ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818c", ENV{OFONO_DRIVER}="mbm"
 
+# HP hs2330 Mobile Broadband Module
+ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="271d", ENV{OFONO_DRIVER}="mbm"
+
+# HP hs2320 Mobile Broadband Module
+ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="261d", ENV{OFONO_DRIVER}="mbm"
+
+# HP lc2000 Mobile Broadband Module
+ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="301d", ENV{OFONO_DRIVER}="mbm"
+
+# HP lc2010 Mobile Broadband Module
+ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="2f1d", ENV{OFONO_DRIVER}="mbm"
+
 # Toshiba
 ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130b", ENV{OFONO_DRIVER}="mbm"
 
--- plugins/phonesim.c
+++ plugins/phonesim.c
@@ -167,6 +167,8 @@
 	if (data->calypso)
 		g_at_chat_set_wakeup_command(data->chat, "AT\r", 500, 5000);
 
+	g_at_chat_send(data->chat, "ATE0", NULL, NULL, NULL, NULL);
+
 	g_at_chat_send(data->chat, "AT+CFUN=1", none_prefix,
 					cfun_set_on_cb, modem, NULL);
 }
@@ -248,12 +250,18 @@
 	}
 
 	if (data->use_mux) {
+		g_at_chat_send(data->chat, "ATE0", NULL, NULL, NULL, NULL);
+
 		g_at_mux_setup_gsm0710(data->chat, mux_setup, modem, NULL);
+
 		g_at_chat_unref(data->chat);
 		data->chat = NULL;
+
+		return -EINPROGRESS;
 	}
 
-	g_at_chat_send(data->chat, "AT+CSCS=\"GSM\"", none_prefix, NULL, NULL, NULL);
+	g_at_chat_send(data->chat, "AT+CSCS=\"GSM\"", none_prefix,
+			NULL, NULL, NULL);
 
 	return 0;
 }
--- plugins/smart-messaging.c
+++ plugins/smart-messaging.c
@@ -70,11 +70,11 @@
 }
 
 static GDBusMethodTable smart_messaging_methods[] = {
-	{ "RegisterAgent",    "o",    "",  smart_messaging_register_agent },
-	{ "UnregisterAgent",  "o",    "",  smart_messaging_unregister_agent },
-	{ "SendBusinessCard", "ab",   "o", smart_messaging_send_vcard,
+	{ "RegisterAgent",    "o",     "",  smart_messaging_register_agent },
+	{ "UnregisterAgent",  "o",     "",  smart_messaging_unregister_agent },
+	{ "SendBusinessCard", "say",   "o", smart_messaging_send_vcard,
 						G_DBUS_METHOD_FLAG_ASYNC },
-	{ "SendAppointment",  "ab",   "o", smart_messaging_send_vcal,
+	{ "SendAppointment",  "say",   "o", smart_messaging_send_vcal,
 						G_DBUS_METHOD_FLAG_ASYNC },
 	{ }
 };
--- plugins/udev.c
+++ plugins/udev.c
@@ -55,38 +55,34 @@
 	return NULL;
 }
 
-static const char *get_driver(struct udev_device *udev_device)
+static const char *get_property(struct udev_device *device,
+				char const *property_name)
 {
 	struct udev_list_entry *entry;
-	const char *driver = NULL;
 
-	entry = udev_device_get_properties_list_entry(udev_device);
+	entry = udev_device_get_properties_list_entry(device);
 	while (entry) {
 		const char *name = udev_list_entry_get_name(entry);
 
-		if (g_strcmp0(name, "OFONO_DRIVER") == 0)
-			driver = udev_list_entry_get_value(entry);
+		if (g_strcmp0(name, property_name) == 0)
+			return udev_list_entry_get_value(entry);
 
 		entry = udev_list_entry_get_next(entry);
 	}
 
-	return driver;
+	return NULL;
 }
 
-static const char *get_serial(struct udev_device *udev_device)
+static const char *get_driver(struct udev_device *udev_device)
 {
-	struct udev_list_entry *entry;
-	const char *serial = NULL;
-
-	entry = udev_device_get_properties_list_entry(udev_device);
-	while (entry) {
-		const char *name = udev_list_entry_get_name(entry);
+	return get_property(udev_device, "OFONO_DRIVER");
+}
 
-		if (g_strcmp0(name, "ID_SERIAL_SHORT") == 0)
-			serial = udev_list_entry_get_value(entry);
+static const char *get_serial(struct udev_device *udev_device)
+{
+	const char *serial;
 
-		entry = udev_list_entry_get_next(entry);
-	}
+	serial = get_property(udev_device, "ID_SERIAL_SHORT");
 
 	if (serial != NULL) {
 		unsigned int i, len = strlen(serial);
@@ -147,8 +143,7 @@
 			g_str_has_suffix(desc, "Mini-Card Network Adapter") ||
 			g_str_has_suffix(desc, "Broadband Network Adapter") ||
 			g_str_has_suffix(desc, "Minicard NetworkAdapter")) {
-		devnode = udev_device_get_property_value(udev_device,
-								"INTERFACE");
+		devnode = get_property(udev_device, "INTERFACE");
 		ofono_modem_set_string(modem, NETWORK_INTERFACE, devnode);
 	} else {
 		return;
@@ -194,8 +189,7 @@
 		else if (g_str_has_suffix(type, "Control") == TRUE)
 			ofono_modem_set_string(modem, CONTROL_PORT, devnode);
 	} else if (g_str_equal(subsystem, "net") == TRUE) {
-		devnode = udev_device_get_property_value(udev_device,
-								"INTERFACE");
+		devnode = get_property(udev_device, "INTERFACE");
 		ofono_modem_set_string(modem, NETWORK_INTERFACE, devnode);
 	} else {
 		return;
@@ -353,6 +347,9 @@
 
 			pcui = 1;
 			ofono_modem_set_integer(modem, "PcuiRegistered", pcui);
+		} else if (g_str_equal(type, "NDIS") == TRUE) {
+			devnode = udev_device_get_devnode(udev_device);
+			ofono_modem_set_string(modem, "NDIS", devnode);
 		}
 
 		break;
@@ -426,19 +423,29 @@
 static void add_isi(struct ofono_modem *modem,
 					struct udev_device *udev_device)
 {
-	const char *ifname, *addr;
+	const char *ifname, *type, *addr;
 
 	DBG("modem %p", modem);
 
+	if (ofono_modem_get_string(modem, "Interface"))
+		return;
+
+	addr = get_property(udev_device, "OFONO_ISI_ADDRESS");
+	if (addr != NULL)
+		ofono_modem_set_integer(modem, "Address", atoi(addr));
+
+	if (g_strcmp0(udev_device_get_subsystem(udev_device), "net") != 0)
+		return;
+
+	type = udev_device_get_sysattr_value(udev_device, "type");
+	if (g_strcmp0(type, "820") != 0)
+		return;
+
 	ifname = udev_device_get_sysname(udev_device);
 	ofono_modem_set_string(modem, "Interface", ifname);
 
 	DBG("interface %s", ifname);
 
-	addr = udev_device_get_property_value(udev_device, "OFONO_ISI_ADDRESS");
-	if (addr != NULL)
-		ofono_modem_set_integer(modem, "Address", atoi(addr));
-
 	ofono_modem_register(modem);
 }
 
--- src/call-settings.c
+++ src/call-settings.c
@@ -551,7 +551,7 @@
 	case CALL_SETTING_TYPE_COLR:
 		set_colr(cs, status);
 		value = colr_status_to_string(status);
-		context = "CallingLineRestriction";
+		context = "CalledLineRestriction";
 		break;
 
 	default:
--- src/common.c
+++ src/common.c
@@ -336,27 +336,39 @@
 	case 19:
 		cls = BEARER_CLASS_VOICE | BEARER_CLASS_FAX;
 		break;
-
-	/* 22.030: 7-11 */
-	/* 22.004 only defines BS 7 (Data Sync) & BS 8 (Data Async) */
+	/*
+	 * 22.030: 7-11
+	 * 22.004 only defines BS 7 (Data Sync) & BS 8 (Data Async)
+	 * and PAD and Packet bearer services are deprecated.  Still,
+	 * AT modems rely on these to differentiate between sending
+	 * a 'All Sync' or 'All Data Sync' message types.  In theory
+	 * both message types cover the same bearer services, but we
+	 * must still send these for conformance reasons.
+	 */
 	case 20:
-		cls = BEARER_CLASS_DATA_ASYNC | BEARER_CLASS_DATA_SYNC;
+		cls = BEARER_CLASS_DATA_ASYNC | BEARER_CLASS_DATA_SYNC |
+			BEARER_CLASS_PAD | BEARER_CLASS_PACKET;
 		break;
-	/* According to 22.030: All Async */
+	/* According to 22.030: All Async (7) */
 	case 21:
-	/* According to 22.030: All Data Async */
+		cls = BEARER_CLASS_DATA_ASYNC | BEARER_CLASS_PAD;
+		break;
+	/* According to 22.030: All Data Async (7)*/
 	case 25:
 		cls = BEARER_CLASS_DATA_ASYNC;
 		break;
-	/* According to 22.030: All Sync */
+	/* According to 22.030: All Sync (8) */
 	case 22:
-	/* According to 22.030: All Data Sync */
+		cls = BEARER_CLASS_DATA_SYNC | BEARER_CLASS_PACKET;
+		break;
+	/* According to 22.030: All Data Sync (8) */
 	case 24:
 		cls = BEARER_CLASS_DATA_SYNC;
 		break;
-	/* According to 22.030: Telephony & All Sync services */
+	/* According to 22.030: Telephony & All Sync services (1, 8) */
 	case 26:
-		cls = BEARER_CLASS_VOICE | BEARER_CLASS_DATA_SYNC;
+		cls = BEARER_CLASS_VOICE | BEARER_CLASS_DATA_SYNC |
+			BEARER_CLASS_PACKET;
 		break;
 	default:
 		break;
@@ -393,7 +405,7 @@
 	}
 }
 
-int valid_ussd_string(const char *str)
+gboolean valid_ussd_string(const char *str, gboolean call_in_progress)
 {
 	int len = strlen(str);
 
@@ -401,44 +413,38 @@
 		return FALSE;
 
 	/*
-	 * It is hard to understand exactly what constitutes a valid USSD string
-	 * According to 22.090:
-	 * Case a - 1, 2 or 3 digits from the set (*, #) followed by 1X(Y),
-	 * where X=any number 0‑4, Y=any number 0‑9, then, optionally "*
-	 * followed by any number of any characters", and concluding with #SEND
-	 *
-	 * Case b - 1, 2 or 3 digits from the set (*, #) followed by 1X(Y),
-	 * where X=any number 5‑9, Y=any number 0‑9, then, optionally "*
-	 * followed by any number of any characters", and concluding with #SEND
+	 * Return true if an MMI input string is to be sent as USSD.
 	 *
-	 * Case c - 7(Y) SEND, where Y=any number 0‑9
-	 *
-	 * Case d - All other formats
-	 *
-	 * According to 22.030 Figure 3.5.3.2 USSD strings can be:
-	 *
-	 * Supplementary service control
-	 * SIM control
-	 * Manufacturer defined
-	 * Terminated by '#'
-	 * Short String - This can be any 2 digit short string.  If the string
-	 *                starts with a '1' and no calls are in progress then
-	 *                this string is treated as a call setup request
-	 *
-	 * Everything else is not a valid USSD string
+	 * According to 3GPP TS 22.030, after checking the well-known
+	 * supplementary service control, SIM control and manufacturer
+	 * defined control codes, the terminal should check if the input
+	 * should be sent as USSD according to the following rules:
+	 *
+	 * 1) Terminated by '#'
+	 * 2) A short string of 1 or 2 digits
+	 *
+	 * As an exception, if a 2 digit string starts with a '1' and
+	 * there are no calls in progress then this string is treated as
+	 * a call setup request instead.
 	 */
 
-	if (len != 2 && str[len-1] != '#')
+	if (str[len-1] == '#')
+		return TRUE;
+
+	if (!call_in_progress && len == 2 && str[0] != '1')
 		return FALSE;
 
-	return TRUE;
+	if (len <= 2)
+		return TRUE;
+
+	return FALSE;
 }
 
 const char *ss_control_type_to_string(enum ss_control_type type)
 {
 	switch (type) {
 	case SS_CONTROL_TYPE_ACTIVATION:
-		return "acivation";
+		return "activation";
 	case SS_CONTROL_TYPE_REGISTRATION:
 		return "registration";
 	case SS_CONTROL_TYPE_QUERY:
--- src/common.h
+++ src/common.h
@@ -130,7 +130,7 @@
 
 int mmi_service_code_to_bearer_class(int code);
 
-gboolean valid_ussd_string(const char *str);
+gboolean valid_ussd_string(const char *str, gboolean call_in_progress);
 
 gboolean parse_ss_control_string(char *str, int *ss_type,
 					char **sc, char **sia,
--- src/main.c
+++ src/main.c
@@ -114,9 +114,9 @@
 				G_OPTION_ARG_CALLBACK, parse_debug,
 				"Specify debug options to enable", "DEBUG" },
 	{ "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin,
-				"Specify plugins to load", "NAME" },
+				"Specify plugins to load", "NAME,..," },
 	{ "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin,
-				"Specify plugins not to load", "NAME" },
+				"Specify plugins not to load", "NAME,..." },
 	{ "nodetach", 'n', G_OPTION_FLAG_REVERSE,
 				G_OPTION_ARG_NONE, &option_detach,
 				"Don't run as daemon in background" },
--- src/modem.c
+++ src/modem.c
@@ -330,6 +330,8 @@
 	GSList *prev;
 	GSList *tmp;
 
+	DBG("");
+
 	prev = NULL;
 	cur = modem->atoms;
 
@@ -367,6 +369,8 @@
 	enum modem_state old_state = modem->modem_state;
 	ofono_bool_t new_online = new_state == MODEM_STATE_ONLINE;
 
+	DBG("old state: %d, new state: %d", old_state, new_state);
+
 	if (old_state == new_state)
 		return;
 
--- src/network.c
+++ src/network.c
@@ -1399,14 +1399,6 @@
 	if (netreg->signal_strength == strength)
 		return;
 
-	/*
-	 * Theoretically we can get signal strength even when not registered
-	 * to any network.  However, what do we do with it in that case?
-	 */
-	if (netreg->status != NETWORK_REGISTRATION_STATUS_REGISTERED &&
-		netreg->status != NETWORK_REGISTRATION_STATUS_ROAMING)
-		return;
-
 	netreg->signal_strength = strength;
 
 	if (strength != -1) {
--- src/ofono.h
+++ src/ofono.h
@@ -207,6 +207,7 @@
 };
 
 typedef void (*ofono_voicecall_dial_cb_t)(struct ofono_call *call, void *data);
+typedef void (*ofono_voicecall_tone_cb_t)(int error, void *data);
 
 ofono_bool_t __ofono_voicecall_is_busy(struct ofono_voicecall *vc,
 					enum ofono_voicecall_interaction type);
@@ -218,6 +219,11 @@
 				ofono_voicecall_dial_cb_t cb, void *user_data);
 void __ofono_voicecall_dial_cancel(struct ofono_voicecall *vc);
 
+int __ofono_voicecall_tone_send(struct ofono_voicecall *vc,
+				const char *tone_str,
+				ofono_voicecall_tone_cb_t cb, void *user_data);
+void __ofono_voicecall_tone_cancel(struct ofono_voicecall *vc, int id);
+
 #include <ofono/sms.h>
 
 struct sms;
@@ -229,12 +235,37 @@
 };
 
 typedef void (*ofono_sms_txq_submit_cb_t)(gboolean ok, void *data);
+typedef void (*ofono_sms_text_notify_cb_t)(const char *from,
+						const struct tm *remote,
+						const struct tm *local,
+						const char *text,
+						void *data);
+typedef void (*ofono_sms_datagram_notify_cb_t)(const char *from,
+						const struct tm *remote,
+						const struct tm *local,
+						int dst, int src,
+						const unsigned char *buffer,
+						unsigned int len,
+						void *data);
 
 int __ofono_sms_txq_submit(struct ofono_sms *sms, GSList *list,
 				unsigned int flags, struct ofono_uuid *uuid,
 				ofono_sms_txq_submit_cb_t cb,
 				void *data, ofono_destroy_func destroy);
 
+unsigned int __ofono_sms_text_watch_add(struct ofono_sms *sms,
+					ofono_sms_text_notify_cb_t cb,
+					void *data, ofono_destroy_func destroy);
+gboolean __ofono_sms_text_watch_remove(struct ofono_sms *sms,
+					unsigned int id);
+
+unsigned int __ofono_sms_datagram_watch_add(struct ofono_sms *sms,
+					ofono_sms_datagram_notify_cb_t cb,
+					int dst, int src, void *data,
+					ofono_destroy_func destroy);
+gboolean __ofono_sms_datagram_watch_remove(struct ofono_sms *sms,
+					unsigned int id);
+
 #include <ofono/sim.h>
 #include <ofono/stk.h>
 
--- src/ofono.service.in
+++ src/ofono.service.in
+[Unit]
+Description=Telephony service
+After=syslog.target
+
+[Service]
+Type=dbus
+BusName=org.ofono
+ExecStart=@prefix@/sbin/ofonod -n
+
+[Install]
+WantedBy=multi-user.target
--- src/plugin.c
+++ src/plugin.c
@@ -72,18 +72,26 @@
 }
 
 static gboolean check_plugin(struct ofono_plugin_desc *desc,
-				const char *pattern, const char *exclude)
+				char **patterns, char **excludes)
 {
-	if (exclude != NULL &&
-			g_pattern_match_simple(exclude, desc->name) == TRUE) {
-		ofono_info("Excluding %s", desc->description);
-		return FALSE;
+	if (excludes) {
+		for (; *excludes; excludes++)
+			if (g_pattern_match_simple(*excludes, desc->name))
+				break;
+		if (*excludes) {
+			ofono_info("Excluding %s", desc->description);
+			return FALSE;
+		}
 	}
 
-	if (pattern != NULL &&
-			g_pattern_match_simple(pattern, desc->name) == FALSE) {
-		ofono_info("Ignoring %s", desc->description);
-		return FALSE;
+	if (patterns) {
+		for (; *patterns; patterns++)
+			if (g_pattern_match_simple(*patterns, desc->name))
+				break;
+		if (!*patterns) {
+			ofono_info("Ignoring %s", desc->description);
+			return FALSE;
+		}
 	}
 
 	return TRUE;
@@ -93,6 +101,8 @@
 
 int __ofono_plugin_init(const char *pattern, const char *exclude)
 {
+	gchar **patterns = NULL;
+	gchar **excludes = NULL;
 	GSList *list;
 	GDir *dir;
 	const gchar *file;
@@ -101,9 +111,15 @@
 
 	DBG("");
 
+	if (pattern)
+		patterns = g_strsplit_set(pattern, ", ", -1);
+
+	if (exclude)
+		excludes = g_strsplit_set(exclude, ", ", -1);
+
 	for (i = 0; __ofono_builtin[i]; i++) {
 		if (check_plugin(__ofono_builtin[i],
-					pattern, exclude) == FALSE)
+					patterns, excludes) == FALSE)
 			continue;
 
 		add_plugin(NULL, __ofono_builtin[i]);
@@ -139,7 +155,7 @@
 				continue;
 			}
 
-			if (check_plugin(desc, pattern, exclude) == FALSE) {
+			if (check_plugin(desc, patterns, excludes) == FALSE) {
 				dlclose(handle);
 				continue;
 			}
--- src/radio-settings.c
+++ src/radio-settings.c
@@ -40,16 +40,16 @@
 struct ofono_radio_settings {
 	DBusMessage *pending;
 	int flags;
-	int mode;
-	int pending_mode;
+	enum ofono_radio_access_mode mode;
+	enum ofono_radio_access_mode pending_mode;
 	const struct ofono_radio_settings_driver *driver;
 	void *driver_data;
 	struct ofono_atom *atom;
 };
 
-static const char *radio_access_mode_to_string(enum ofono_radio_access_mode mode)
+static const char *radio_access_mode_to_string(enum ofono_radio_access_mode m)
 {
-	switch (mode) {
+	switch (m) {
 	case OFONO_RADIO_ACCESS_MODE_ANY:
 		return "any";
 	case OFONO_RADIO_ACCESS_MODE_GSM:
@@ -63,18 +63,25 @@
 	}
 }
 
-static int string_to_radio_access_mode(const char *mode)
+static gboolean radio_access_mode_from_string(const char *str,
+					enum ofono_radio_access_mode *mode)
 
 {
-	if (g_strcmp0(mode, "any") == 0)
-		return OFONO_RADIO_ACCESS_MODE_ANY;
-	if (g_strcmp0(mode, "gsm") == 0)
-		return OFONO_RADIO_ACCESS_MODE_GSM;
-	if (g_strcmp0(mode, "umts") == 0)
-		return OFONO_RADIO_ACCESS_MODE_UMTS;
-	if (g_strcmp0(mode, "lte") == 0)
-		return OFONO_RADIO_ACCESS_MODE_LTE;
-	return -1;
+	if (g_str_equal(str, "any")) {
+		*mode = OFONO_RADIO_ACCESS_MODE_ANY;
+		return TRUE;
+	} else if (g_str_equal(str, "gsm")) {
+		*mode = OFONO_RADIO_ACCESS_MODE_GSM;
+		return TRUE;
+	} else if (g_str_equal(str, "umts")) {
+		*mode = OFONO_RADIO_ACCESS_MODE_UMTS;
+		return TRUE;
+	} else if (g_str_equal(str, "lte")) {
+		*mode = OFONO_RADIO_ACCESS_MODE_LTE;
+		return TRUE;
+	}
+
+	return FALSE;
 }
 
 static DBusMessage *radio_get_properties_reply(DBusMessage *msg,
@@ -96,8 +103,8 @@
 					OFONO_PROPERTIES_ARRAY_SIGNATURE,
 					&dict);
 
-	ofono_dbus_dict_append(&dict, "TechnologyPreference", DBUS_TYPE_STRING,
-				&mode);
+	ofono_dbus_dict_append(&dict, "TechnologyPreference",
+					DBUS_TYPE_STRING, &mode);
 
 	dbus_message_iter_close_container(&iter, &dict);
 
@@ -111,7 +118,7 @@
 	const char *path;
 	const char *str_mode;
 
-	if (rs->mode == (int)mode)
+	if (rs->mode == mode)
 		return;
 
 	rs->mode = mode;
@@ -121,9 +128,9 @@
 	str_mode = radio_access_mode_to_string(rs->mode);
 
 	ofono_dbus_signal_property_changed(conn, path,
-					OFONO_RADIO_SETTINGS_INTERFACE,
-					"TechnologyPreference", DBUS_TYPE_STRING,
-					&str_mode);
+						OFONO_RADIO_SETTINGS_INTERFACE,
+						"TechnologyPreference",
+						DBUS_TYPE_STRING, &str_mode);
 }
 
 static void radio_mode_set_callback(const struct ofono_error *error, void *data)
@@ -165,8 +172,8 @@
 	__ofono_dbus_pending_reply(&rs->pending, reply);
 }
 
-static DBusMessage *radio_get_properties(DBusConnection *conn, DBusMessage *msg,
-					void *data)
+static DBusMessage *radio_get_properties(DBusConnection *conn,
+						DBusMessage *msg, void *data)
 {
 	struct ofono_radio_settings *rs = data;
 
@@ -212,7 +219,7 @@
 
 	if (g_strcmp0(property, "TechnologyPreference") == 0) {
 		const char *value;
-		int mode = -1;
+		enum ofono_radio_access_mode mode;
 
 		if (!rs->driver->set_rat_mode)
 			return __ofono_error_not_implemented(msg);
@@ -221,9 +228,7 @@
 			return __ofono_error_invalid_args(msg);
 
 		dbus_message_iter_get_basic(&var, &value);
-		mode = string_to_radio_access_mode(value);
-
-		if (mode == -1)
+		if (radio_access_mode_from_string(value, &mode) == FALSE)
 			return __ofono_error_invalid_args(msg);
 
 		if (rs->mode == mode)
@@ -241,10 +246,10 @@
 }
 
 static GDBusMethodTable radio_methods[] = {
-	{ "GetProperties",	"",	"a{sv}",	radio_get_properties,
-							G_DBUS_METHOD_FLAG_ASYNC },
-	{ "SetProperty",	"sv",	"",		radio_set_property,
-							G_DBUS_METHOD_FLAG_ASYNC },
+	{ "GetProperties",  "",    "a{sv}",  radio_get_properties,
+						G_DBUS_METHOD_FLAG_ASYNC },
+	{ "SetProperty",    "sv",  "",       radio_set_property,
+						G_DBUS_METHOD_FLAG_ASYNC },
 	{ }
 };
 
--- src/sim.c
+++ src/sim.c
@@ -43,6 +43,7 @@
 #include "simutil.h"
 #include "storage.h"
 #include "simfs.h"
+#include "stkutil.h"
 
 static GSList *g_drivers = NULL;
 
@@ -71,6 +72,7 @@
 	unsigned char efest_length;
 	unsigned char *efsst;
 	unsigned char efsst_length;
+	gboolean fixed_dialing;
 
 	char *imsi;
 
@@ -90,6 +92,8 @@
 
 	struct sim_fs *simfs;
 
+	unsigned char *iidf_image;
+
 	DBusMessage *pending;
 	const struct ofono_sim_driver *driver;
 	void *driver_data;
@@ -283,6 +287,7 @@
 	char **locked_pins;
 	const char *pin_name;
 	dbus_bool_t present = sim->state != OFONO_SIM_STATE_NOT_PRESENT;
+	dbus_bool_t fdn;
 
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
@@ -307,7 +312,10 @@
 		ofono_dbus_dict_append(&dict, "SubscriberIdentity",
 					DBUS_TYPE_STRING, &sim->imsi);
 
-	if (sim->mnc_length) {
+	fdn = sim->fixed_dialing;
+	ofono_dbus_dict_append(&dict, "FixedDialing", DBUS_TYPE_BOOLEAN, &fdn);
+
+	if (sim->mnc_length && sim->imsi) {
 		char mcc[OFONO_MAX_MCC_LENGTH + 1];
 		char mnc[OFONO_MAX_MNC_LENGTH + 1];
 		const char *str;
@@ -721,6 +729,181 @@
 	return NULL;
 }
 
+static void sim_get_image_cb(struct ofono_sim *sim,
+				unsigned char id, char *xpm, gboolean cache)
+{
+	DBusMessage *reply;
+	DBusMessageIter iter, array;
+	int xpm_len;
+
+	if (xpm == NULL) {
+		reply = __ofono_error_failed(sim->pending);
+		__ofono_dbus_pending_reply(&sim->pending, reply);
+		return;
+	}
+
+	xpm_len = strlen(xpm);
+
+	reply = dbus_message_new_method_return(sim->pending);
+	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,
+						&xpm, xpm_len);
+	dbus_message_iter_close_container(&iter, &array);
+
+	__ofono_dbus_pending_reply(&sim->pending, reply);
+
+	if (cache)
+		sim_fs_cache_image(sim->simfs, (const char *) xpm, id);
+
+	g_free(xpm);
+}
+
+static void sim_iidf_read_clut_cb(int ok, int length, int record,
+					const unsigned char *data,
+					int record_length, void *userdata)
+{
+	struct ofono_sim *sim = userdata;
+	unsigned char id;
+	unsigned char *efimg;
+	unsigned short iidf_len;
+	unsigned short clut_len;
+	char *xpm;
+
+	DBG("ok: %d", ok);
+
+	dbus_message_get_args(sim->pending, NULL, DBUS_TYPE_BYTE, &id,
+					DBUS_TYPE_INVALID);
+	id -= 1;
+	efimg = &sim->efimg[id * 9];
+
+	if (!ok) {
+		sim_get_image_cb(sim, id, NULL, FALSE);
+		goto done;
+	}
+
+	iidf_len = efimg[7] << 8 | efimg[8];
+
+	if (sim->iidf_image[3] == 0)
+		clut_len = 256 * 3;
+	else
+		clut_len = sim->iidf_image[3] * 3;
+
+	xpm = stk_image_to_xpm(sim->iidf_image, iidf_len, efimg[2],
+					data, clut_len);
+	sim_get_image_cb(sim, id, xpm, TRUE);
+
+done:
+	g_free(sim->iidf_image);
+	sim->iidf_image = NULL;
+}
+
+static void sim_iidf_read_cb(int ok, int length, int record,
+				const unsigned char *data,
+				int record_length, void *userdata)
+{
+	struct ofono_sim *sim = userdata;
+	unsigned char id;
+	unsigned char *efimg;
+	unsigned short iidf_id;
+	unsigned short offset;
+	unsigned short clut_len;
+
+	DBG("ok: %d", ok);
+
+	dbus_message_get_args(sim->pending, NULL, DBUS_TYPE_BYTE, &id,
+					DBUS_TYPE_INVALID);
+	id -= 1;
+	efimg = &sim->efimg[id * 9];
+
+	if (!ok) {
+		sim_get_image_cb(sim, id, NULL, FALSE);
+		return;
+	}
+
+	if (efimg[2] == STK_IMG_SCHEME_BASIC) {
+		char *xpm = stk_image_to_xpm(data, length, efimg[2], NULL, 0);
+		sim_get_image_cb(sim, id, xpm, TRUE);
+		return;
+	}
+
+	offset = data[4] << 8 | data[5];
+
+	if (data[3] == 0)
+		clut_len = 256 * 3;
+	else
+		clut_len = data[3] * 3;
+
+	iidf_id = efimg[3] << 8 | efimg[4];
+	sim->iidf_image = g_memdup(data, length);
+
+	/* read the clut data */
+	ofono_sim_read_bytes(sim, iidf_id, offset, clut_len,
+					sim_iidf_read_clut_cb, sim);
+}
+
+static void sim_get_image(struct ofono_sim *sim, unsigned char id,
+				gpointer user_data)
+{
+	unsigned char *efimg;
+	char *image;
+	unsigned short iidf_id;
+	unsigned short iidf_offset;
+	unsigned short iidf_len;
+
+	image = sim_fs_get_cached_image(sim->simfs, id);
+
+	if (image != NULL) {
+		sim_get_image_cb(sim, id, image, FALSE);
+		return;
+	}
+
+	if (sim->efimg_length <= (id * 9)) {
+		sim_get_image_cb(sim, id, NULL, FALSE);
+		return;
+	}
+
+	efimg = &sim->efimg[id * 9];
+
+	iidf_id = efimg[3] << 8 | efimg[4];
+	iidf_offset = efimg[5] << 8 | efimg[6];
+	iidf_len = efimg[7] << 8 | efimg[8];
+
+	/* read the image data */
+	ofono_sim_read_bytes(sim, iidf_id, iidf_offset, iidf_len,
+				sim_iidf_read_cb, sim);
+}
+
+static DBusMessage *sim_get_icon(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct ofono_sim *sim = data;
+	unsigned char id;
+
+	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &id,
+					DBUS_TYPE_INVALID) == FALSE)
+		return __ofono_error_invalid_args(msg);
+
+	/* zero means no icon */
+	if (id == 0)
+		return __ofono_error_invalid_args(msg);
+
+	if (sim->pending)
+		return __ofono_error_busy(msg);
+
+	if (sim->efimg == NULL)
+		return __ofono_error_not_implemented(msg);
+
+	sim->pending = dbus_message_ref(msg);
+
+	sim_get_image(sim, id - 1, sim);
+
+	return NULL;
+}
+
 static DBusMessage *sim_reset_pin(DBusConnection *conn, DBusMessage *msg,
 					void *data)
 {
@@ -773,6 +956,8 @@
 							G_DBUS_METHOD_FLAG_ASYNC },
 	{ "UnlockPin",		"ss",	"",		sim_unlock_pin,
 							G_DBUS_METHOD_FLAG_ASYNC },
+	{ "GetIcon",		"y",	"ay",		sim_get_icon,
+							G_DBUS_METHOD_FLAG_ASYNC },
 	{ }
 };
 
@@ -1061,6 +1246,39 @@
 	sim->driver->read_imsi(sim, sim_imsi_cb, sim);
 }
 
+static void sim_fdn_enabled(struct ofono_sim *sim)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	const char *path = __ofono_atom_get_path(sim->atom);
+	dbus_bool_t val;
+
+	sim->fixed_dialing = TRUE;
+
+	val = sim->fixed_dialing;
+	ofono_dbus_signal_property_changed(conn, path,
+						OFONO_SIM_MANAGER_INTERFACE,
+						"FixedDialing",
+						DBUS_TYPE_BOOLEAN, &val);
+}
+
+static void sim_efadn_info_read_cb(int ok, unsigned char file_status,
+					int total_length, int record_length,
+					void *userdata)
+{
+	struct ofono_sim *sim = userdata;
+
+	if (!ok)
+		goto out;
+
+	if (file_status != SIM_FILE_STATUS_VALID) {
+		sim_fdn_enabled(sim);
+		return;
+	}
+
+out:
+	sim_retrieve_imsi(sim);
+}
+
 static void sim_efsst_read_cb(int ok, int length, int record,
 				const unsigned char *data,
 				int record_length, void *userdata)
@@ -1078,6 +1296,20 @@
 	sim->efsst = g_memdup(data, length);
 	sim->efsst_length = length;
 
+	/*
+	 * Check if Fixed Dialing is enabled in the SIM-card
+	 * (TS 11.11/TS 51.011, Section 11.5.1: FDN capability request).
+	 * If FDN is activated and ADN is invalidated,
+	 * don't continue initialization routine.
+	 */
+	if (sim_sst_is_active(sim->efsst, sim->efsst_length,
+				SIM_SST_SERVICE_FDN)) {
+		sim_fs_read_info(sim->simfs, SIM_EFADN_FILEID,
+					OFONO_SIM_FILE_STRUCTURE_FIXED,
+					sim_efadn_info_read_cb, sim);
+		return;
+	}
+
 out:
 	sim_retrieve_imsi(sim);
 }
@@ -1099,6 +1331,17 @@
 	sim->efest = g_memdup(data, length);
 	sim->efest_length = length;
 
+	/*
+	 * Check if Fixed Dialing is enabled in the USIM-card
+	 * (TS 31.102, Section 5.3.2: FDN capability request).
+	 * If FDN is activated, don't continue initialization routine.
+	 */
+	if (sim_est_is_active(sim->efest, sim->efest_length,
+				SIM_EST_SERVICE_FDN)) {
+		sim_fdn_enabled(sim);
+		return;
+	}
+
 out:
 	sim_retrieve_imsi(sim);
 }
@@ -1177,10 +1420,34 @@
 {
 	struct ofono_sim *sim = userdata;
 
-	if (!ok || length != 1)
+	if (!ok || length != 1) {
 		sim->phase = OFONO_SIM_PHASE_3G;
-	else
-		sim->phase = data[0];
+
+		ofono_sim_read(sim, SIM_EFUST_FILEID,
+				OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+				sim_efust_read_cb, sim);
+
+		return;
+	}
+
+	switch (data[0]) {
+	case 0:
+		sim->phase = OFONO_SIM_PHASE_1G;
+		break;
+	case 2:
+		sim->phase = OFONO_SIM_PHASE_2G;
+		break;
+	case 3:
+		sim->phase = OFONO_SIM_PHASE_2G_PLUS;
+		break;
+	default:
+		ofono_error("Unknown phase");
+		return;
+	}
+
+	ofono_sim_read(sim, SIM_EFSST_FILEID,
+			OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+			sim_efsst_read_cb, sim);
 }
 
 static void sim_initialize_after_pin(struct ofono_sim *sim)
@@ -1200,16 +1467,6 @@
 	ofono_sim_read(sim, SIM_EF_CPHS_INFORMATION_FILEID,
 			OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
 			sim_cphs_information_read_cb, sim);
-
-	/* Also retrieve the GSM service table */
-	if (sim->phase >= OFONO_SIM_PHASE_3G)
-		ofono_sim_read(sim, SIM_EFUST_FILEID,
-				OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
-				sim_efust_read_cb, sim);
-	else
-		ofono_sim_read(sim, SIM_EFSST_FILEID,
-				OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
-				sim_efsst_read_cb, sim);
 }
 
 static void sim_pin_query_cb(const struct ofono_error *error,
@@ -1601,11 +1858,6 @@
 
 static void sim_free_state(struct ofono_sim *sim)
 {
-	if (sim->simfs) {
-		sim_fs_free(sim->simfs);
-		sim->simfs = NULL;
-	}
-
 	if (sim->iccid) {
 		g_free(sim->iccid);
 		sim->iccid = NULL;
@@ -1665,6 +1917,11 @@
 		sim->efimg = NULL;
 		sim->efimg_length = 0;
 	}
+
+	g_free(sim->iidf_image);
+	sim->iidf_image = NULL;
+
+	sim->fixed_dialing = FALSE;
 }
 
 void ofono_sim_inserted_notify(struct ofono_sim *sim, ofono_bool_t inserted)
@@ -1803,6 +2060,11 @@
 
 	sim_free_state(sim);
 
+	if (sim->simfs) {
+		sim_fs_free(sim->simfs);
+		sim->simfs = NULL;
+	}
+
 	g_free(sim);
 }
 
--- src/simfs.c
+++ src/simfs.c
@@ -47,6 +47,8 @@
 #define SIM_CACHE_PATH SIM_CACHE_BASEPATH "/%04x"
 #define SIM_CACHE_HEADER_SIZE 38
 #define SIM_FILE_INFO_SIZE 6
+#define SIM_IMAGE_CACHE_BASEPATH STORAGEDIR "/%s-%i/images"
+#define SIM_IMAGE_CACHE_PATH SIM_IMAGE_CACHE_BASEPATH "/%d.xpm"
 
 #define SIM_FS_VERSION 1
 
@@ -56,8 +58,10 @@
 
 struct sim_fs_op {
 	int id;
+	unsigned char *buffer;
 	enum ofono_sim_file_structure structure;
 	unsigned short offset;
+	gboolean info_only;
 	int num_bytes;
 	int length;
 	int record_length;
@@ -69,6 +73,7 @@
 
 static void sim_fs_op_free(struct sim_fs_op *node)
 {
+	g_free(node->buffer);
 	g_free(node);
 }
 
@@ -77,7 +82,6 @@
 	gint op_source;
 	unsigned char bitmap[32];
 	int fd;
-	unsigned char *buffer;
 	struct ofono_sim *sim;
 	const struct ofono_sim_driver *driver;
 };
@@ -129,9 +133,6 @@
 		fs->fd = -1;
 	}
 
-	g_free(fs->buffer);
-	fs->buffer = NULL;
-
 	memset(fs->bitmap, 0, sizeof(fs->bitmap));
 
 	sim_fs_op_free(op);
@@ -228,15 +229,19 @@
 	if (op->current == start_block) {
 		bufoff = 0;
 		dataoff = op->offset % 256;
-		tocopy = MIN(256 - op->offset % 256, len);
+		tocopy = MIN(256 - op->offset % 256,
+				op->num_bytes - op->current * 256);
 	} else {
 		bufoff = (op->current - start_block - 1) * 256 +
 				op->offset % 256;
 		dataoff = 0;
-		tocopy = len;
+		tocopy = MIN(256, op->num_bytes - op->current * 256);
 	}
 
-	memcpy(fs->buffer + bufoff, data + dataoff, tocopy);
+	DBG("bufoff: %d, dataoff: %d, tocopy: %d",
+				bufoff, dataoff, tocopy);
+
+	memcpy(op->buffer + bufoff, data + dataoff, tocopy);
 	cache_block(fs, op->current, 256, data, len);
 
 	op->current++;
@@ -244,7 +249,7 @@
 	if (op->current > end_block) {
 		ofono_sim_file_read_cb_t cb = op->cb;
 
-		cb(1, op->num_bytes, 0, fs->buffer,
+		cb(1, op->num_bytes, 0, op->buffer,
 				op->record_length, op->userdata);
 
 		sim_fs_end_current(fs);
@@ -265,9 +270,9 @@
 	end_block = (op->offset + (op->num_bytes - 1)) / 256;
 
 	if (op->current == start_block) {
-		fs->buffer = g_try_new0(unsigned char, op->num_bytes);
+		op->buffer = g_try_new0(unsigned char, op->num_bytes);
 
-		if (fs->buffer == NULL) {
+		if (op->buffer == NULL) {
 			sim_fs_op_error(fs);
 			return FALSE;
 		}
@@ -288,18 +293,21 @@
 			seekoff = SIM_CACHE_HEADER_SIZE + op->current * 256 +
 				op->offset % 256;
 			toread = MIN(256 - op->offset % 256,
-					op->length - op->current * 256);
+					op->num_bytes - op->current * 256);
 		} else {
 			bufoff = (op->current - start_block - 1) * 256 +
 					op->offset % 256;
 			seekoff = SIM_CACHE_HEADER_SIZE + op->current * 256;
-			toread = MIN(256, op->length - op->current * 256);
+			toread = MIN(256, op->num_bytes - op->current * 256);
 		}
 
+		DBG("bufoff: %d, seekoff: %d, toread: %d",
+				bufoff, seekoff, toread);
+
 		if (lseek(fs->fd, seekoff, SEEK_SET) == (off_t) -1)
 			break;
 
-		if (TFR(read(fs->fd, fs->buffer + bufoff, toread)) != toread)
+		if (TFR(read(fs->fd, op->buffer + bufoff, toread)) != toread)
 			break;
 
 		op->current += 1;
@@ -308,7 +316,7 @@
 	if (op->current > end_block) {
 		ofono_sim_file_read_cb_t cb = op->cb;
 
-		cb(1, op->num_bytes, 0, fs->buffer,
+		cb(1, op->num_bytes, 0, op->buffer,
 				op->record_length, op->userdata);
 
 		sim_fs_end_current(fs);
@@ -426,7 +434,9 @@
 static void sim_fs_op_info_cb(const struct ofono_error *error, int length,
 				enum ofono_sim_file_structure structure,
 				int record_length,
-				const unsigned char access[3], void *data)
+				const unsigned char access[3],
+				unsigned char file_status,
+				void *data)
 {
 	struct sim_fs *fs = data;
 	struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
@@ -473,14 +483,33 @@
 
 		op->record_length = length;
 		op->current = op->offset / 256;
-		fs->op_source = g_idle_add(sim_fs_op_read_block, fs);
+
+		if (op->info_only == FALSE)
+			fs->op_source = g_idle_add(sim_fs_op_read_block, fs);
 	} else {
 		op->record_length = record_length;
 		op->current = 1;
-		fs->op_source = g_idle_add(sim_fs_op_read_record, fs);
+
+		if (op->info_only == FALSE)
+			fs->op_source = g_idle_add(sim_fs_op_read_record, fs);
+	}
+
+	if (op->info_only == TRUE) {
+		/*
+		 * It's info-only request. So there is no need to request
+		 * actual contents of the EF-files. Just return the EF-info.
+		 */
+		sim_fs_read_info_cb_t cb = op->cb;
+
+		cb(1, file_status, op->length,
+			op->record_length, op->userdata);
+
+		sim_fs_end_current(fs);
+
+		return;
 	}
 
-	if (imsi == NULL || cache == FALSE)
+	if (imsi == NULL || phase == OFONO_SIM_PHASE_UNKNOWN || cache == FALSE)
 		return;
 
 	memset(fileinfo, 0, SIM_CACHE_HEADER_SIZE);
@@ -522,7 +551,7 @@
 	enum ofono_sim_file_structure structure;
 	int record_length;
 
-	if (!imsi)
+	if (imsi == NULL || op->info_only == TRUE)
 		return FALSE;
 
 	path = g_strdup_printf(SIM_CACHE_PATH, imsi, phase, op->id);
@@ -608,17 +637,17 @@
 		switch (op->structure) {
 		case OFONO_SIM_FILE_STRUCTURE_TRANSPARENT:
 			driver->write_file_transparent(fs->sim, op->id, 0,
-					op->length, fs->buffer,
+					op->length, op->buffer,
 					sim_fs_op_write_cb, fs);
 			break;
 		case OFONO_SIM_FILE_STRUCTURE_FIXED:
 			driver->write_file_linear(fs->sim, op->id, op->current,
-					op->length, fs->buffer,
+					op->length, op->buffer,
 					sim_fs_op_write_cb, fs);
 			break;
 		case OFONO_SIM_FILE_STRUCTURE_CYCLIC:
 			driver->write_file_cyclic(fs->sim, op->id,
-					op->length, fs->buffer,
+					op->length, op->buffer,
 					sim_fs_op_write_cb, fs);
 			break;
 		default:
@@ -626,13 +655,50 @@
 					"this can't happen");
 		}
 
-		g_free(fs->buffer);
-		fs->buffer = NULL;
+		g_free(op->buffer);
+		op->buffer = NULL;
 	}
 
 	return FALSE;
 }
 
+int sim_fs_read_info(struct sim_fs *fs, int id,
+			enum ofono_sim_file_structure expected_type,
+			sim_fs_read_info_cb_t cb, void *data)
+{
+	struct sim_fs_op *op;
+
+	if (!cb)
+		return -EINVAL;
+
+	if (!fs->driver)
+		return -EINVAL;
+
+	if (!fs->driver->read_file_info)
+		return -ENOSYS;
+
+	if (!fs->op_q)
+		fs->op_q = g_queue_new();
+
+	op = g_try_new0(struct sim_fs_op, 1);
+	if (op == NULL)
+		return -ENOMEM;
+
+	op->id = id;
+	op->structure = expected_type;
+	op->cb = cb;
+	op->userdata = data;
+	op->is_read = TRUE;
+	op->info_only = TRUE;
+
+	g_queue_push_tail(fs->op_q, op);
+
+	if (g_queue_get_length(fs->op_q) == 1)
+		fs->op_source = g_idle_add(sim_fs_op_next, fs);
+
+	return 0;
+}
+
 int sim_fs_read(struct sim_fs *fs, int id,
 		enum ofono_sim_file_structure expected_type,
 		unsigned short offset, unsigned short num_bytes,
@@ -641,18 +707,20 @@
 	struct sim_fs_op *op;
 
 	if (!cb)
-		return -1;
+		return -EINVAL;
 
 	if (!fs->driver)
-		return -1;
+		return -EINVAL;
 
 	if (!fs->driver->read_file_info)
-		return -1;
+		return -ENOSYS;
 
 	if (!fs->op_q)
 		fs->op_q = g_queue_new();
 
-	op = g_new0(struct sim_fs_op, 1);
+	op = g_try_new0(struct sim_fs_op, 1);
+	if (op == NULL)
+		return -ENOMEM;
 
 	op->id = id;
 	op->structure = expected_type;
@@ -661,6 +729,7 @@
 	op->is_read = TRUE;
 	op->offset = offset;
 	op->num_bytes = num_bytes;
+	op->info_only = FALSE;
 
 	g_queue_push_tail(fs->op_q, op);
 
@@ -678,10 +747,10 @@
 	gconstpointer fn = NULL;
 
 	if (!cb)
-		return -1;
+		return -EINVAL;
 
 	if (!fs->driver)
-		return -1;
+		return -EINVAL;
 
 	switch (structure) {
 	case OFONO_SIM_FILE_STRUCTURE_TRANSPARENT:
@@ -698,18 +767,20 @@
 	}
 
 	if (fn == NULL)
-		return -1;
+		return -ENOSYS;
 
 	if (!fs->op_q)
 		fs->op_q = g_queue_new();
 
-	op = g_new0(struct sim_fs_op, 1);
+	op = g_try_new0(struct sim_fs_op, 1);
+	if (op == NULL)
+		return -ENOMEM;
 
 	op->id = id;
 	op->cb = cb;
 	op->userdata = userdata;
 	op->is_read = FALSE;
-	fs->buffer = g_memdup(data, length);
+	op->buffer = g_memdup(data, length);
 	op->structure = structure;
 	op->length = length;
 	op->current = record;
@@ -722,6 +793,74 @@
 	return 0;
 }
 
+void sim_fs_cache_image(struct sim_fs *fs, const char *image, int id)
+{
+	const char *imsi;
+	enum ofono_sim_phase phase;
+
+	if (fs == NULL || image == NULL)
+		return;
+
+	imsi = ofono_sim_get_imsi(fs->sim);
+	if (imsi == NULL)
+		return;
+
+	phase = ofono_sim_get_phase(fs->sim);
+	if (phase == OFONO_SIM_PHASE_UNKNOWN)
+		return;
+
+	write_file((const unsigned char *) image, strlen(image),
+			SIM_CACHE_MODE, SIM_IMAGE_CACHE_PATH, imsi,
+			phase, id);
+}
+
+char *sim_fs_get_cached_image(struct sim_fs *fs, int id)
+{
+	const char *imsi;
+	enum ofono_sim_phase phase;
+	unsigned short image_length;
+	int fd;
+	char *buffer;
+	char *path;
+	int len;
+	struct stat st_buf;
+
+	if (fs == NULL)
+		return NULL;
+
+	imsi = ofono_sim_get_imsi(fs->sim);
+	if (imsi == NULL)
+		return NULL;
+
+	phase = ofono_sim_get_phase(fs->sim);
+	path = g_strdup_printf(SIM_IMAGE_CACHE_PATH, imsi, phase, id);
+
+	TFR(stat(path, &st_buf));
+	fd = TFR(open(path, O_RDONLY));
+	g_free(path);
+
+	if (fd < 0)
+		return NULL;
+
+	image_length = st_buf.st_size;
+	buffer = g_try_new0(char, image_length + 1);
+
+	if (buffer == NULL) {
+		TFR(close(fd));
+		return NULL;
+	}
+
+	len = TFR(read(fd, buffer, image_length));
+	TFR(close(fd));
+
+	if (len != image_length) {
+		g_free(buffer);
+		return NULL;
+	}
+
+	return buffer;
+}
+
 static void remove_cachefile(const char *imsi, enum ofono_sim_phase phase,
 				const struct dirent *file)
 {
@@ -739,6 +878,23 @@
 	g_free(path);
 }
 
+static void remove_imagefile(const char *imsi, enum ofono_sim_phase phase,
+				const struct dirent *file)
+{
+	int id;
+	char *path;
+
+	if (file->d_type != DT_REG)
+		return;
+
+	if (sscanf(file->d_name, "%d", &id) != 1)
+		return;
+
+	path = g_strdup_printf(SIM_IMAGE_CACHE_PATH, imsi, phase, id);
+	remove(path);
+	g_free(path);
+}
+
 void sim_fs_check_version(struct sim_fs *fs)
 {
 	const char *imsi = ofono_sim_get_imsi(fs->sim);
@@ -765,6 +921,20 @@
 			g_free(entries[len]);
 		}
 
+		g_free(entries);
+	}
+
+	path = g_strdup_printf(SIM_IMAGE_CACHE_BASEPATH, imsi, phase);
+	len = scandir(path, &entries, NULL, alphasort);
+	g_free(path);
+
+	if (len > 0) {
+		/* Remove everything */
+		while (len--) {
+			remove_imagefile(imsi, phase, entries[len]);
+			g_free(entries[len]);
+		}
+
 		g_free(entries);
 	}
 
--- src/simfs.h
+++ src/simfs.h
@@ -21,6 +21,10 @@
 
 struct sim_fs;
 
+typedef void (*sim_fs_read_info_cb_t)(int ok, unsigned char file_status,
+					int total_length, int record_length,
+					void *userdata);
+
 struct sim_fs *sim_fs_new(struct ofono_sim *sim,
 				const struct ofono_sim_driver *driver);
 
@@ -29,10 +33,18 @@
 		unsigned short offset, unsigned short num_bytes,
 		ofono_sim_file_read_cb_t cb, void *data);
 
+int sim_fs_read_info(struct sim_fs *fs, int id,
+		enum ofono_sim_file_structure expected_type,
+		sim_fs_read_info_cb_t cb, void *data);
+
 void sim_fs_check_version(struct sim_fs *fs);
 
 int sim_fs_write(struct sim_fs *fs, int id, ofono_sim_file_write_cb_t cb,
 			enum ofono_sim_file_structure structure, int record,
 			const unsigned char *data, int length, void *userdata);
 
+char *sim_fs_get_cached_image(struct sim_fs *fs, int id);
+
+void sim_fs_cache_image(struct sim_fs *fs, const char *image, int id);
+
 void sim_fs_free(struct sim_fs *fs);
--- src/simutil.c
+++ src/simutil.c
@@ -69,6 +69,7 @@
 {	0x2F05, ROOTMF, BINARY, 0,	ALW,	PIN	},
 {	0x2F06, ROOTMF, RECORD, 0,	ALW,	PIN	},
 {	0x2FE2, ROOTMF, BINARY, 10,	ALW,	NEV 	},
+{	0x4F20, 0x5F50, BINARY, 0,	PIN,	ADM	},
 {	0x6F05, 0x7F20, BINARY, 0,	ALW,	PIN	},
 {	0x6F06, 0x0000, RECORD, 0,	ALW,	ADM	},
 {	0x6F2C, 0x7F20, BINARY, 16,	PIN,	PIN	},
@@ -1405,7 +1406,8 @@
 
 gboolean sim_parse_2g_get_response(const unsigned char *response, int len,
 					int *file_len, int *record_len,
-					int *structure, unsigned char *access)
+					int *structure, unsigned char *access,
+					unsigned char *file_status)
 {
 	if (len < 14 || response[6] != 0x04)
 		return FALSE;
@@ -1420,6 +1422,8 @@
 	access[1] = response[9];
 	access[2] = response[10];
 
+	*file_status = response[11];
+
 	if (response[13] == 0x01 || response[13] == 0x03)
 		*record_len = response[14];
 	else
--- src/simutil.h
+++ src/simutil.h
@@ -32,6 +32,7 @@
 	SIM_EFMSISDN_FILEID = 0x6f40,
 	SIM_EFSPN_FILEID = 0x6f46,
 	SIM_EFSDN_FILEID = 0x6f49,
+	SIM_EFADN_FILEID = 0x6f3a,
 	SIM_EFEST_FILEID = 0x6f56,
 	SIM_EFAD_FILEID = 0x6fad,
 	SIM_EFPHASE_FILEID = 0x6fae,
@@ -57,6 +58,12 @@
 	SIM_FILE_ACCESS_NEVER = 15,
 };
 
+/* 51.011 Section 9.3 */
+enum sim_file_status {
+	SIM_FILE_STATUS_VALID			= 0x01,
+	SIM_FILE_STATUS_RW_WHEN_INVALID		= 0x04,
+};
+
 /* 131.102 Section 4.2.8 */
 enum sim_ust_service {
 	SIM_UST_SERVICE_LOCAL_PHONE_BOOK		= 0,
@@ -425,7 +432,8 @@
 
 gboolean sim_parse_2g_get_response(const unsigned char *response, int len,
 					int *file_len, int *record_len,
-					int *structure, unsigned char *access);
+					int *structure, unsigned char *access,
+					unsigned char *file_status);
 
 gboolean sim_ust_is_available(unsigned char *service_ust, unsigned char len,
 						enum sim_ust_service index);
--- src/sms.c
+++ src/sms.c
@@ -62,6 +62,12 @@
 	enum message_state state;
 };
 
+struct sms_handler {
+	struct ofono_watchlist_item item;
+	int dst;
+	int src;
+};
+
 struct ofono_sms {
 	int flags;
 	DBusMessage *pending;
@@ -69,7 +75,7 @@
 	struct sms_assembly *assembly;
 	guint ref;
 	GQueue *txq;
-	gint tx_source;
+	guint tx_source;
 	struct ofono_message_waiting *mw;
 	unsigned int mw_watch;
 	struct ofono_sim *sim;
@@ -82,6 +88,8 @@
 	ofono_bool_t use_delivery_reports;
 	struct status_report_assembly *sr_assembly;
 	GHashTable *messages;
+	struct ofono_watchlist *text_handlers;
+	struct ofono_watchlist *datagram_handlers;
 };
 
 struct pending_pdu {
@@ -108,6 +116,11 @@
 	return memcmp(v1, v2, OFONO_SHA1_UUID_LEN) == 0;
 }
 
+static gboolean port_equal(int received, int expected)
+{
+	return expected == -1 || received == expected;
+}
+
 static guint uuid_hash(gconstpointer v)
 {
 	const struct ofono_uuid *uuid = v;
@@ -170,6 +183,77 @@
 	ofono_dbus_dict_append(dict, "State", DBUS_TYPE_STRING, &state);
 }
 
+static unsigned int add_sms_handler(struct ofono_watchlist *watchlist,
+					int dst, int src, void *notify,
+					void *data, ofono_destroy_func destroy)
+{
+	struct sms_handler *handler;
+
+	if (!notify)
+		return 0;
+
+	handler = g_try_new0(struct sms_handler, 1);
+	if (!handler)
+		return 0;
+
+	handler->dst = dst;
+	handler->src = src;
+	handler->item.notify = notify;
+	handler->item.notify_data = data;
+	handler->item.destroy = destroy;
+
+	return __ofono_watchlist_add_item(watchlist,
+				(struct ofono_watchlist_item *)handler);
+}
+
+unsigned int __ofono_sms_text_watch_add(struct ofono_sms *sms,
+					ofono_sms_text_notify_cb_t cb,
+					void *data, ofono_destroy_func destroy)
+{
+	if (!sms)
+		return 0;
+
+	DBG("%p", sms);
+
+	return add_sms_handler(sms->text_handlers, -1, -1, cb, data, destroy);
+}
+
+gboolean __ofono_sms_text_watch_remove(struct ofono_sms *sms,
+					unsigned int id)
+{
+	if (!sms)
+		return FALSE;
+
+	DBG("%p", sms);
+
+	return __ofono_watchlist_remove_item(sms->text_handlers, id);
+}
+
+unsigned int __ofono_sms_datagram_watch_add(struct ofono_sms *sms,
+					ofono_sms_datagram_notify_cb_t cb,
+					int dst, int src, void *data,
+					ofono_destroy_func destroy)
+{
+	if (!sms)
+		return 0;
+
+	DBG("%p: dst %d, src %d", sms, dst, src);
+
+	return add_sms_handler(sms->datagram_handlers, dst, src, cb, data,
+				destroy);
+}
+
+gboolean __ofono_sms_datagram_watch_remove(struct ofono_sms *sms,
+					unsigned int id)
+{
+	if (!sms)
+		return FALSE;
+
+	DBG("%p", sms);
+
+	return __ofono_watchlist_remove_item(sms->datagram_handlers, id);
+}
+
 static DBusMessage *message_get_properties(DBusConnection *conn,
 						DBusMessage *msg, void *data)
 {
@@ -825,7 +909,7 @@
 	int i = 0;
 	GSList *l;
 
-	entry= g_try_new0(struct tx_queue_entry, 1);
+	entry = g_try_new0(struct tx_queue_entry, 1);
 	if (entry == NULL)
 		return NULL;
 
@@ -1064,12 +1148,35 @@
 	return TRUE;
 }
 
-static void dispatch_app_datagram(struct ofono_sms *sms, int dst, int src,
-					unsigned char *buf, long len)
+static void dispatch_app_datagram(struct ofono_sms *sms,
+					const struct ofono_uuid *uuid,
+					int dst, int src,
+					unsigned char *buf, unsigned len,
+					const struct sms_address *addr,
+					const struct sms_scts *scts)
 {
-	DBG("Got app datagram for dst port: %d, src port: %d",
-			dst, src);
-	DBG("Contents-Len: %ld", len);
+	const char *sender = sms_address_to_string(addr);
+	time_t ts;
+	struct tm remote;
+	struct tm local;
+
+	ofono_sms_datagram_notify_cb_t notify;
+	struct sms_handler *h;
+	GSList *l;
+
+	ts = sms_scts_to_time(scts, &remote);
+	localtime_r(&ts, &local);
+
+	for (l = sms->datagram_handlers->items; l; l = l->next) {
+		h = l->data;
+		notify = h->item.notify;
+
+		if (!port_equal(dst, h->dst) || !port_equal(src, h->src))
+			continue;
+
+		notify(sender, &remote, &local, dst, src, buf, len,
+			h->item.notify_data);
+	}
 }
 
 static void dispatch_text_message(struct ofono_sms *sms,
@@ -1091,6 +1198,9 @@
 	struct tm remote;
 	struct tm local;
 	const char *str = buf;
+	ofono_sms_text_notify_cb_t notify;
+	struct sms_handler *h;
+	GSList *l;
 
 	if (!message)
 		return;
@@ -1132,15 +1242,25 @@
 
 	g_dbus_send_message(conn, signal);
 
-	if (cls != SMS_CLASS_0)
-		__ofono_history_sms_received(modem, uuid, str,
-						&remote, &local, message);
+	if (cls == SMS_CLASS_0)
+		return;
+
+	for (l = sms->text_handlers->items; l; l = l->next) {
+		h = l->data;
+		notify = h->item.notify;
+
+		notify(str, &remote, &local, message, h->item.notify_data);
+	}
+
+	__ofono_history_sms_received(modem, uuid, str, &remote, &local,
+					message);
 }
 
 static void sms_dispatch(struct ofono_sms *sms, GSList *sms_list)
 {
 	GSList *l;
 	const struct sms *s;
+	struct ofono_uuid uuid;
 	enum sms_charset uninitialized_var(old_charset);
 	enum sms_class cls;
 	int srcport = -1;
@@ -1214,6 +1334,11 @@
 		}
 	}
 
+	if (!compute_incoming_msgid(sms_list, &uuid))
+		return;
+
+	s = sms_list->data;
+
 	/* Handle datagram */
 	if (old_charset == SMS_CHARSET_8BIT) {
 		unsigned char *buf;
@@ -1230,23 +1355,18 @@
 		if (!buf)
 			return;
 
-		dispatch_app_datagram(sms, dstport, srcport, buf, len);
+		dispatch_app_datagram(sms, &uuid, dstport, srcport, buf, len,
+					&s->deliver.oaddr, &s->deliver.scts);
 
 		g_free(buf);
 	} else {
-		struct ofono_uuid uuid;
 		char *message = sms_decode_text(sms_list);
 
 		if (!message)
 			return;
 
-		if (compute_incoming_msgid(sms_list, &uuid)) {
-			s = sms_list->data;
-
-			dispatch_text_message(sms, &uuid, message, cls,
-						&s->deliver.oaddr,
-						&s->deliver.scts);
-		}
+		dispatch_text_message(sms, &uuid, message, cls,
+					&s->deliver.oaddr, &s->deliver.scts);
 
 		g_free(message);
 	}
@@ -1527,6 +1647,12 @@
 		g_hash_table_destroy(sms->messages);
 		sms->messages = NULL;
 	}
+
+	__ofono_watchlist_free(sms->text_handlers);
+	sms->text_handlers = NULL;
+
+	__ofono_watchlist_free(sms->datagram_handlers);
+	sms->datagram_handlers = NULL;
 }
 
 static void sms_remove(struct ofono_atom *atom)
@@ -1741,6 +1867,9 @@
 		sms->driver->bearer_set(sms, sms->bearer,
 						bearer_init_callback, sms);
 
+	sms->text_handlers = __ofono_watchlist_new(g_free);
+	sms->datagram_handlers = __ofono_watchlist_new(g_free);
+
 	__ofono_atom_register(sms->atom, sms_unregister);
 }
 
--- src/stk.c
+++ src/stk.c
@@ -70,9 +70,11 @@
 	gboolean respond_on_exit;
 	ofono_bool_t immediate_response;
 	guint remove_agent_source;
-	struct sms_submit_req *sms_submit_req;
+	struct extern_req *extern_req;
 	char *idle_mode_text;
+	struct stk_icon_id idle_mode_icon;
 	struct timeval get_inkey_start_ts;
+	int dtmf_id;
 };
 
 struct envelope_op {
@@ -83,7 +85,7 @@
 			const unsigned char *data, int length);
 };
 
-struct sms_submit_req {
+struct extern_req {
 	struct ofono_stk *stk;
 	gboolean cancelled;
 };
@@ -255,30 +257,81 @@
 		stk_cbs_download_cb(stk, FALSE, NULL, -1);
 }
 
+static char *dbus_apply_text_attributes(const char *text,
+					const struct stk_text_attribute *attr)
+{
+	uint16_t buf[256], *i = buf;
+	const uint8_t *j = attr->attributes;
+	const uint8_t *end = j + attr->len;
+
+	if (attr->len & 3)
+		return NULL;
+
+	while (j < end)
+		*i++ = *j++;
+
+	return stk_text_to_html(text, buf, attr->len / 4);
+}
+
 static struct stk_menu *stk_menu_create(const char *title,
-		const struct stk_text_attribute *title_attr, GSList *items,
+		const struct stk_text_attribute *title_attr,
+		const struct stk_icon_id *icon, GSList *items,
 		const struct stk_item_text_attribute_list *item_attrs,
+		const struct stk_item_icon_id_list *item_icon_ids,
 		int default_id, gboolean soft_key, gboolean has_help)
 {
-	struct stk_menu *ret = g_new(struct stk_menu, 1);
+	unsigned int len = g_slist_length(items);
+	struct stk_menu *ret;
 	GSList *l;
 	int i;
+	struct stk_text_attribute attr;
 
 	DBG("");
 
-	ret->title = g_strdup(title ? title : "");
-	ret->icon_id = 0;
-	ret->items = g_new0(struct stk_menu_item, g_slist_length(items) + 1);
+	if (item_attrs && item_attrs->len && item_attrs->len != len * 4)
+		return NULL;
+
+	if (item_icon_ids && item_icon_ids->len && item_icon_ids->len != len)
+		return NULL;
+
+	ret = g_try_new(struct stk_menu, 1);
+	if (ret == NULL)
+		return NULL;
+
+	ret->title = dbus_apply_text_attributes(title ? title : "",
+						title_attr);
+	if (!ret->title)
+		ret->title = g_strdup(title ? title : "");
+
+	memcpy(&ret->icon, icon, sizeof(ret->icon));
+	ret->items = g_new0(struct stk_menu_item, len + 1);
 	ret->default_item = -1;
 	ret->soft_key = soft_key;
 	ret->has_help = has_help;
 
 	for (l = items, i = 0; l; l = l->next, i++) {
 		struct stk_item *item = l->data;
+		char *text;
 
-		ret->items[i].text = g_strdup(item->text);
 		ret->items[i].item_id = item->id;
 
+		text = NULL;
+
+		if (item_attrs && item_attrs->len) {
+			memcpy(attr.attributes, &item_attrs->list[i * 4], 4);
+			attr.len = 4;
+
+			text = dbus_apply_text_attributes(item->text, &attr);
+		}
+
+		if (!text)
+			text = strdup(item->text);
+
+		ret->items[i].text = text;
+
+		if (item_icon_ids && item_icon_ids->len)
+			ret->items[i].icon_id = item_icon_ids->list[i];
+
 		if (item->id == default_id)
 			ret->default_item = i;
 	}
@@ -294,8 +347,10 @@
 
 	return stk_menu_create(cmd->setup_menu.alpha_id,
 				&cmd->setup_menu.text_attr,
+				&cmd->setup_menu.icon_id,
 				cmd->setup_menu.items,
 				&cmd->setup_menu.item_text_attr_list,
+				&cmd->setup_menu.item_icon_id_list,
 				0, soft_key, has_help);
 }
 
@@ -307,8 +362,10 @@
 
 	return stk_menu_create(cmd->select_item.alpha_id,
 				&cmd->select_item.text_attr,
+				&cmd->select_item.icon_id,
 				cmd->select_item.items,
 				&cmd->select_item.item_text_attr_list,
+				&cmd->select_item.item_icon_id_list,
 				cmd->select_item.item_id, soft_key, has_help);
 }
 
@@ -345,6 +402,11 @@
 						"MainMenuTitle",
 						DBUS_TYPE_STRING, &menu->title);
 
+	ofono_dbus_signal_property_changed(conn, path,
+						OFONO_STK_INTERFACE,
+						"MainMenuIcon",
+						DBUS_TYPE_BYTE, &menu->icon.id);
+
 	signal = dbus_message_new_signal(path, OFONO_STK_INTERFACE,
 						"PropertyChanged");
 	if (!signal) {
@@ -363,25 +425,9 @@
 	g_dbus_send_message(conn, signal);
 }
 
-static void dict_append_menu(DBusMessageIter *dict, struct stk_menu *menu)
-{
-	DBusMessageIter entry;
-	const char *key = "MainMenu";
-
-	ofono_dbus_dict_append(dict, "MainMenuTitle",
-				DBUS_TYPE_STRING, &menu->title);
-
-	dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
-						NULL, &entry);
-
-	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
-
-	append_menu_items_variant(&entry, menu->items);
-
-	dbus_message_iter_close_container(dict, &entry);
-}
-
-static void stk_alpha_id_set(struct ofono_stk *stk, const char *text)
+static void stk_alpha_id_set(struct ofono_stk *stk,
+		const char *text, const struct stk_text_attribute *attr,
+		const struct stk_icon_id *icon)
 {
 	/* TODO */
 }
@@ -398,7 +444,10 @@
 	DBusMessage *reply;
 	DBusMessageIter iter;
 	DBusMessageIter dict;
-	const char *idle_mode_text;
+	DBusMessageIter entry;
+	const char *key = "MainMenu";
+	const char *str;
+	unsigned char icon;
 
 	reply = dbus_message_new_method_return(msg);
 	if (!reply)
@@ -410,13 +459,26 @@
 					OFONO_PROPERTIES_ARRAY_SIGNATURE,
 					&dict);
 
-	idle_mode_text = stk->idle_mode_text ? stk->idle_mode_text : "";
-	ofono_dbus_dict_append(&dict, "IdleModeText",
-				DBUS_TYPE_STRING, &idle_mode_text);
+	str = stk->idle_mode_text ? stk->idle_mode_text : "";
+	ofono_dbus_dict_append(&dict, "IdleModeText", DBUS_TYPE_STRING, &str);
+
+	icon = stk->idle_mode_icon.id;
+	ofono_dbus_dict_append(&dict, "IdleModeIcon", DBUS_TYPE_BYTE, &icon);
+
+	str = stk->main_menu ? stk->main_menu->title : "";
+	ofono_dbus_dict_append(&dict, "MainMenuTitle", DBUS_TYPE_STRING, &str);
+
+	icon = stk->main_menu ? stk->main_menu->icon.id : 0;
+	ofono_dbus_dict_append(&dict, "MainMenuIcon", DBUS_TYPE_BYTE, &icon);
+
+	dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+						NULL, &entry);
+	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
 
-	if (stk->main_menu)
-		dict_append_menu(&dict, stk->main_menu);
+	append_menu_items_variant(&entry,
+				stk->main_menu ? stk->main_menu->items : NULL);
 
+	dbus_message_iter_close_container(&dict, &entry);
 	dbus_message_iter_close_container(&iter, &dict);
 
 	return reply;
@@ -435,8 +497,12 @@
 {
 	struct ofono_stk *stk = user_data;
 
-	if (stk->current_agent == stk->default_agent && stk->respond_on_exit)
+	if (stk->current_agent == stk->default_agent && stk->respond_on_exit) {
+		if (stk->pending_cmd)
+			stk->cancel_cmd(stk);
+
 		send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED);
+	}
 
 	stk->default_agent = NULL;
 	stk->current_agent = stk->session_agent;
@@ -450,6 +516,9 @@
 	DBG("Session Agent removed");
 
 	if (stk->current_agent == stk->session_agent && stk->respond_on_exit) {
+		if (stk->pending_cmd)
+			stk->cancel_cmd(stk);
+
 		DBG("Sending Terminate response for session agent");
 		send_simple_response(stk, STK_RESULT_TYPE_USER_TERMINATED);
 	}
@@ -660,18 +729,14 @@
 
 static void send_sms_cancel(struct ofono_stk *stk)
 {
-	stk->sms_submit_req->cancelled = TRUE;
-
-	if (!stk->pending_cmd->send_sms.alpha_id ||
-			!stk->pending_cmd->send_sms.alpha_id[0])
-		return;
+	stk->extern_req->cancelled = TRUE;
 
 	stk_alpha_id_unset(stk);
 }
 
 static void send_sms_submit_cb(gboolean ok, void *data)
 {
-	struct sms_submit_req *req = data;
+	struct extern_req *req = data;
 	struct ofono_stk *stk = req->stk;
 	struct ofono_error failure = { .type = OFONO_ERROR_TYPE_FAILURE };
 	struct stk_response rsp;
@@ -684,9 +749,7 @@
 		return;
 	}
 
-	if (stk->pending_cmd->send_sms.alpha_id &&
-			stk->pending_cmd->send_sms.alpha_id[0])
-		stk_alpha_id_unset(stk);
+	stk_alpha_id_unset(stk);
 
 	memset(&rsp, 0, sizeof(rsp));
 
@@ -697,6 +760,12 @@
 		stk_command_cb(&failure, stk);
 }
 
+static void extern_req_start(struct ofono_stk *stk)
+{
+	stk->extern_req = g_new0(struct extern_req, 1);
+	stk->extern_req->stk = stk;
+}
+
 static gboolean handle_command_send_sms(const struct stk_command *cmd,
 					struct stk_response *rsp,
 					struct ofono_stk *stk)
@@ -715,23 +784,22 @@
 
 	sms = __ofono_atom_get_data(sms_atom);
 
-	stk->sms_submit_req = g_new0(struct sms_submit_req, 1);
-	stk->sms_submit_req->stk = stk;
+	extern_req_start(stk);
 
 	msg_list.data = (void *) &cmd->send_sms.gsm_sms;
 	msg_list.next = NULL;
 
 	if (__ofono_sms_txq_submit(sms, &msg_list, 0, NULL, send_sms_submit_cb,
-				stk->sms_submit_req, g_free) < 0) {
-		g_free(stk->sms_submit_req);
+				stk->extern_req, g_free) < 0) {
+		g_free(stk->extern_req);
 		rsp->result.type = STK_RESULT_TYPE_TERMINAL_BUSY;
 		return TRUE;
 	}
 
 	stk->cancel_cmd = send_sms_cancel;
 
-	if (cmd->send_sms.alpha_id && cmd->send_sms.alpha_id[0])
-		stk_alpha_id_set(stk, cmd->send_sms.alpha_id);
+	stk_alpha_id_set(stk, cmd->send_sms.alpha_id, &cmd->send_sms.text_attr,
+				&cmd->send_sms.icon_id);
 
 	return FALSE;
 }
@@ -742,22 +810,40 @@
 {
 	DBusConnection *conn = ofono_dbus_get_connection();
 	const char *path = __ofono_atom_get_path(stk->atom);
-	const char *idle_mode_text;
+	char *idle_mode_text = NULL;
 
-	if (stk->idle_mode_text) {
-		g_free(stk->idle_mode_text);
-		stk->idle_mode_text = NULL;
+	if (cmd->setup_idle_mode_text.text) {
+		idle_mode_text = dbus_apply_text_attributes(
+					cmd->setup_idle_mode_text.text,
+					&cmd->setup_idle_mode_text.text_attr);
+
+		if (!idle_mode_text) {
+			rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+			return TRUE;
+		}
 	}
 
-	if (cmd->setup_idle_mode_text.text)
-		stk->idle_mode_text = g_strdup(cmd->setup_idle_mode_text.text);
+	if (stk->idle_mode_text)
+		g_free(stk->idle_mode_text);
+
+	stk->idle_mode_text = idle_mode_text;
 
-	idle_mode_text = stk->idle_mode_text ? stk->idle_mode_text : "";
+	idle_mode_text = idle_mode_text ? idle_mode_text : "";
 	ofono_dbus_signal_property_changed(conn, path, OFONO_STK_INTERFACE,
 						"IdleModeText",
 						DBUS_TYPE_STRING,
 						&idle_mode_text);
 
+	if (stk->idle_mode_icon.id != cmd->setup_idle_mode_text.icon_id.id) {
+		memcpy(&stk->idle_mode_icon, &cmd->setup_idle_mode_text.icon_id,
+				sizeof(stk->idle_mode_icon));
+
+		ofono_dbus_signal_property_changed(conn, path,
+						OFONO_STK_INTERFACE,
+						"IdleModeIcon", DBUS_TYPE_BYTE,
+						&stk->idle_mode_icon.id);
+	}
+
 	return TRUE;
 }
 
@@ -1125,6 +1211,13 @@
 	struct stk_command_display_text *dt = &stk->pending_cmd->display_text;
 	uint8_t qualifier = stk->pending_cmd->qualifier;
 	ofono_bool_t priority = (qualifier & (1 << 0)) != 0;
+	char *text = dbus_apply_text_attributes(dt->text, &dt->text_attr);
+	int err;
+
+	if (!text) {
+		rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+		return TRUE;
+	}
 
 	if (dt->duration.interval) {
 		timeout = dt->duration.interval;
@@ -1138,10 +1231,13 @@
 		}
 	}
 
+	err = stk_agent_display_text(stk->current_agent, text, &dt->icon_id,
+					priority, display_text_cb, stk,
+					display_text_destroy, timeout);
+	g_free(text);
+
 	/* We most likely got an out of memory error, tell SIM to retry */
-	if (stk_agent_display_text(stk->current_agent, dt->text, 0, priority,
-					display_text_cb, stk,
-					display_text_destroy, timeout) < 0) {
+	if (err < 0) {
 		rsp->result.type = STK_RESULT_TYPE_TERMINAL_BUSY;
 		return TRUE;
 	}
@@ -1275,6 +1371,7 @@
 {
 	int timeout = stk->timeout * 1000;
 	const struct stk_command_get_inkey *gi = &cmd->get_inkey;
+	char *text = dbus_apply_text_attributes(gi->text, &gi->text_attr);
 	uint8_t qualifier = stk->pending_cmd->qualifier;
 	gboolean alphabet = (qualifier & (1 << 0)) != 0;
 	gboolean ucs2 = (qualifier & (1 << 1)) != 0;
@@ -1283,9 +1380,13 @@
 	 * Note: immediate response and help parameter values are not
 	 * provided by current api.
 	 */
-	uint8_t icon_id = 0;
 	int err;
 
+	if (!text) {
+		rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+		return TRUE;
+	}
+
 	if (gi->duration.interval) {
 		timeout = gi->duration.interval;
 		switch (gi->duration.unit) {
@@ -1302,18 +1403,21 @@
 
 	if (yesno)
 		err = stk_agent_request_confirmation(stk->current_agent,
-							gi->text, icon_id,
+							text, &gi->icon_id,
 							request_confirmation_cb,
 							stk, NULL, timeout);
 	else if (alphabet)
-		err = stk_agent_request_key(stk->current_agent, gi->text,
-						icon_id, ucs2, request_key_cb,
-						stk, NULL, timeout);
+		err = stk_agent_request_key(stk->current_agent, text,
+						&gi->icon_id, ucs2,
+						request_key_cb, stk, NULL,
+						timeout);
 	else
-		err = stk_agent_request_digit(stk->current_agent, gi->text,
-						icon_id, request_key_cb,
+		err = stk_agent_request_digit(stk->current_agent, text,
+						&gi->icon_id, request_key_cb,
 						stk, NULL, timeout);
 
+	g_free(text);
+
 	if (err < 0) {
 		/*
 		 * We most likely got an out of memory error, tell SIM
@@ -1373,28 +1477,35 @@
 {
 	int timeout = stk->timeout * 1000;
 	const struct stk_command_get_input *gi = &cmd->get_input;
+	char *text = dbus_apply_text_attributes(gi->text, &gi->text_attr);
 	uint8_t qualifier = stk->pending_cmd->qualifier;
 	gboolean alphabet = (qualifier & (1 << 0)) != 0;
 	gboolean ucs2 = (qualifier & (1 << 1)) != 0;
 	gboolean hidden = (qualifier & (1 << 2)) != 0;
-	uint8_t icon_id = 0;
 	int err;
 
+	if (!text) {
+		rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+		return TRUE;
+	}
+
 	if (alphabet)
-		err = stk_agent_request_input(stk->current_agent, gi->text,
-						icon_id, gi->default_text, ucs2,
-						gi->resp_len.min,
+		err = stk_agent_request_input(stk->current_agent, text,
+						&gi->icon_id, gi->default_text,
+						ucs2, gi->resp_len.min,
 						gi->resp_len.max, hidden,
 						request_string_cb,
 						stk, NULL, timeout);
 	else
-		err = stk_agent_request_digits(stk->current_agent, gi->text,
-						icon_id, gi->default_text,
+		err = stk_agent_request_digits(stk->current_agent, text,
+						&gi->icon_id, gi->default_text,
 						gi->resp_len.min,
 						gi->resp_len.max, hidden,
 						request_string_cb,
 						stk, NULL, timeout);
 
+	g_free(text);
+
 	if (err < 0) {
 		/*
 		 * We most likely got an out of memory error, tell SIM
@@ -1460,6 +1571,7 @@
 	uint8_t qualifier = stk->pending_cmd->qualifier;
 	static unsigned char busy_on_call_result[] = { 0x02 };
 	static unsigned char no_cause_result[] = { 0x00 };
+	char *alpha_id = NULL;
 	struct ofono_voicecall *vc = NULL;
 	struct ofono_atom *vc_atom;
 	struct stk_response rsp;
@@ -1495,10 +1607,22 @@
 		return;
 	}
 
+	if (sc->alpha_id_call_setup) {
+		alpha_id = dbus_apply_text_attributes(sc->alpha_id_call_setup,
+						&sc->text_attr_call_setup);
+		if (!alpha_id) {
+			send_simple_response(stk,
+					STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD);
+			return;
+		}
+	}
+
 	err = __ofono_voicecall_dial(vc, sc->addr.number, sc->addr.ton_npi,
-					sc->alpha_id_call_setup, 0,
+					alpha_id, sc->icon_id_call_setup.id,
 					qualifier >> 1, call_setup_connected,
 					stk);
+	g_free(alpha_id);
+
 	if (err >= 0) {
 		stk->cancel_cmd = call_setup_cancel;
 
@@ -1541,6 +1665,7 @@
 	const struct stk_command_setup_call *sc = &cmd->setup_call;
 	uint8_t qualifier = cmd->qualifier;
 	static unsigned char busy_on_call_result[] = { 0x02 };
+	char *alpha_id = NULL;
 	struct ofono_voicecall *vc = NULL;
 	struct ofono_atom *vc_atom;
 	int err;
@@ -1576,9 +1701,19 @@
 		return TRUE;
 	}
 
-	err = stk_agent_confirm_call(stk->current_agent, sc->alpha_id_usr_cfm,
-					0, confirm_call_cb, stk, NULL,
-					stk->timeout * 1000);
+	if (sc->alpha_id_usr_cfm) {
+		alpha_id = dbus_apply_text_attributes(sc->alpha_id_usr_cfm,
+							&sc->text_attr_usr_cfm);
+		if (!alpha_id) {
+			rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+			return TRUE;
+		}
+	}
+
+	err = stk_agent_confirm_call(stk->current_agent, alpha_id,
+					&sc->icon_id_usr_cfm, confirm_call_cb,
+					stk, NULL, stk->timeout * 1000);
+	g_free(alpha_id);
 
 	if (err < 0) {
 		/*
@@ -1609,9 +1744,7 @@
 	if (ussd)
 		__ofono_ussd_initiate_cancel(ussd);
 
-	if (stk->pending_cmd->send_ussd.alpha_id &&
-			stk->pending_cmd->send_ussd.alpha_id[0])
-		stk_alpha_id_unset(stk);
+	stk_alpha_id_unset(stk);
 }
 
 static void send_ussd_callback(int error, int dcs, const unsigned char *msg,
@@ -1621,10 +1754,9 @@
 	struct ofono_error failure = { .type = OFONO_ERROR_TYPE_FAILURE };
 	struct stk_response rsp;
 	enum sms_charset charset;
+	unsigned char no_cause[] = { 0x00 };
 
-	if (stk->pending_cmd->send_ussd.alpha_id &&
-			stk->pending_cmd->send_ussd.alpha_id[0])
-		stk_alpha_id_unset(stk);
+	stk_alpha_id_unset(stk);
 
 	memset(&rsp, 0, sizeof(rsp));
 
@@ -1661,7 +1793,13 @@
 		break;
 
 	default:
-		send_simple_response(stk, STK_RESULT_TYPE_USSD_RETURN_ERROR);
+		rsp.result.type = STK_RESULT_TYPE_USSD_RETURN_ERROR;
+		rsp.result.additional_len = sizeof(no_cause);
+		rsp.result.additional = no_cause;
+
+		if (stk_respond(stk, &rsp, stk_command_cb))
+			stk_command_cb(&failure, stk);
+
 		break;
 	}
 }
@@ -1750,8 +1888,9 @@
 		return TRUE;
 	}
 
-	if (cmd->send_ussd.alpha_id && cmd->send_ussd.alpha_id[0])
-		stk_alpha_id_set(stk, cmd->send_ussd.alpha_id);
+	stk_alpha_id_set(stk, cmd->send_ussd.alpha_id,
+				&cmd->send_ussd.text_attr,
+				&cmd->send_ussd.icon_id);
 
 	return FALSE;
 }
@@ -1817,6 +1956,140 @@
 	return TRUE;
 }
 
+static void send_dtmf_cancel(struct ofono_stk *stk)
+{
+	struct ofono_voicecall *vc = NULL;
+	struct ofono_atom *vc_atom;
+
+	stk->respond_on_exit = FALSE;
+
+	vc_atom = __ofono_modem_find_atom(__ofono_atom_get_modem(stk->atom),
+						OFONO_ATOM_TYPE_VOICECALL);
+	if (vc_atom)
+		vc = __ofono_atom_get_data(vc_atom);
+
+	if (vc) /* Should be always true here */
+		__ofono_voicecall_tone_cancel(vc, stk->dtmf_id);
+
+	stk_alpha_id_unset(stk);
+}
+
+static void dtmf_sent_cb(int error, void *user_data)
+{
+	struct ofono_stk *stk = user_data;
+
+	stk->respond_on_exit = FALSE;
+
+	stk_alpha_id_unset(stk);
+
+	if (error == ENOENT) {
+		struct stk_response rsp;
+		static unsigned char not_in_speech_call_result[] = { 0x07 };
+		static struct ofono_error failure =
+			{ .type = OFONO_ERROR_TYPE_FAILURE };
+
+		memset(&rsp, 0, sizeof(rsp));
+
+		rsp.result.type = STK_RESULT_TYPE_TERMINAL_BUSY;
+		rsp.result.additional_len = sizeof(not_in_speech_call_result);
+		rsp.result.additional = not_in_speech_call_result;
+
+		if (stk_respond(stk, &rsp, stk_command_cb))
+			stk_command_cb(&failure, stk);
+
+		return;
+	}
+
+	if (error != 0)
+		send_simple_response(stk, STK_RESULT_TYPE_NOT_CAPABLE);
+	else
+		send_simple_response(stk, STK_RESULT_TYPE_SUCCESS);
+}
+
+static gboolean handle_command_send_dtmf(const struct stk_command *cmd,
+						struct stk_response *rsp,
+						struct ofono_stk *stk)
+{
+	static unsigned char not_in_speech_call_result[] = { 0x07 };
+	struct ofono_voicecall *vc = NULL;
+	struct ofono_atom *vc_atom;
+	char dtmf[256], *digit;
+	char *dtmf_from = "01234567890abcABC";
+	char *dtmf_to = "01234567890*#p*#p";
+	int err, pos;
+
+	vc_atom = __ofono_modem_find_atom(__ofono_atom_get_modem(stk->atom),
+						OFONO_ATOM_TYPE_VOICECALL);
+	if (vc_atom)
+		vc = __ofono_atom_get_data(vc_atom);
+
+	if (!vc) {
+		rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE;
+		return TRUE;
+	}
+
+	/* Convert the DTMF string to phone number format */
+	for (pos = 0; cmd->send_dtmf.dtmf[pos] != '\0'; pos++) {
+		digit = strchr(dtmf_from, cmd->send_dtmf.dtmf[pos]);
+		if (!digit) {
+			rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+			return TRUE;
+		}
+
+		dtmf[pos] = dtmf_to[digit - dtmf_from];
+	}
+
+	dtmf[pos] = '\0';
+
+	err = __ofono_voicecall_tone_send(vc, dtmf, dtmf_sent_cb, stk);
+
+	if (err == -EBUSY) {
+		rsp->result.type = STK_RESULT_TYPE_TERMINAL_BUSY;
+		return TRUE;
+	}
+
+	if (err == -ENOSYS) {
+		rsp->result.type = STK_RESULT_TYPE_NOT_CAPABLE;
+		return TRUE;
+	}
+
+	if (err == -ENOENT) {
+		rsp->result.type = STK_RESULT_TYPE_TERMINAL_BUSY;
+		rsp->result.additional_len = sizeof(not_in_speech_call_result);
+		rsp->result.additional = not_in_speech_call_result;
+		return TRUE;
+	}
+
+	if (err == -EINVAL) {
+		rsp->result.type = STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD;
+		return TRUE;
+	}
+
+	if (err < 0) {
+		/*
+		 * We most likely got an out of memory error, tell SIM
+		 * to retry
+		 */
+		rsp->result.type = STK_RESULT_TYPE_TERMINAL_BUSY;
+		return TRUE;
+	}
+
+	stk_alpha_id_set(stk, cmd->send_dtmf.alpha_id,
+				&cmd->send_dtmf.text_attr,
+				&cmd->send_dtmf.icon_id);
+
+	/*
+	 * Note that we don't strictly require an agent to be connected,
+	 * but to comply with 6.4.24 we need to send a End Session when
+	 * the user decides so.
+	 */
+	stk->respond_on_exit = TRUE;
+	stk->cancel_cmd = send_dtmf_cancel;
+	stk->dtmf_id = err;
+
+	return FALSE;
+}
+
 static void stk_proactive_command_cancel(struct ofono_stk *stk)
 {
 	if (stk->immediate_response)
@@ -1989,6 +2262,11 @@
 							&rsp, stk);
 		break;
 
+	case STK_COMMAND_TYPE_SEND_DTMF:
+		respond = handle_command_send_dtmf(stk->pending_cmd,
+							&rsp, stk);
+		break;
+
 	default:
 		rsp.result.type = STK_RESULT_TYPE_COMMAND_NOT_UNDERSTOOD;
 		break;
@@ -2002,6 +2280,47 @@
 		stk_command_cb(&error, stk);
 }
 
+void ofono_stk_proactive_command_handled_notify(struct ofono_stk *stk,
+						int length,
+						const unsigned char *pdu)
+{
+	struct stk_command *cmd;
+
+	stk_proactive_command_cancel(stk);
+
+	cmd = stk_command_new_from_pdu(pdu, length);
+
+	if (!cmd || cmd->status != STK_PARSE_RESULT_OK) {
+		ofono_error("Can't parse proactive command");
+
+		if (cmd)
+			stk_command_free(cmd);
+		return;
+	}
+
+	DBG("type: %d", cmd->type);
+
+	switch (cmd->type) {
+	case STK_COMMAND_TYPE_MORE_TIME:
+		break;
+
+	case STK_COMMAND_TYPE_SEND_SMS:
+		stk_alpha_id_set(stk, cmd->send_sms.alpha_id,
+				&cmd->send_sms.text_attr,
+				&cmd->send_sms.icon_id);
+		break;
+	}
+
+	stk_command_free(cmd);
+}
+
+void ofono_stk_terminal_response_sent_notify(struct ofono_stk *stk,
+						int length,
+						const unsigned char *pdu)
+{
+	stk_alpha_id_unset(stk);
+}
+
 int ofono_stk_driver_register(const struct ofono_stk_driver *d)
 {
 	DBG("driver: %p, name: %s", d, d->name);
--- src/stkagent.c
+++ src/stkagent.c
@@ -34,6 +34,8 @@
 #include "ofono.h"
 
 #include "common.h"
+#include "smsutil.h"
+#include "stkutil.h"
 #include "stkagent.h"
 
 enum allowed_error {
@@ -238,7 +240,7 @@
 	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
 						"(sy)", &array);
 
-	for (; item->text; item++) {
+	while (item && item->text) {
 		dbus_message_iter_open_container(&array, DBUS_TYPE_STRUCT,
 							NULL, &entry);
 
@@ -248,6 +250,7 @@
 						&item->icon_id);
 
 		dbus_message_iter_close_container(&array, &entry);
+		item++;
 	}
 
 	dbus_message_iter_close_container(iter, &array);
@@ -343,7 +346,7 @@
 	dbus_message_iter_init_append(agent->msg, &iter);
 
 	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &menu->title);
-	dbus_message_iter_append_basic(&iter, DBUS_TYPE_BYTE, &menu->icon_id);
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_BYTE, &menu->icon.id);
 	append_menu_items(&iter, menu->items);
 	dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT16, &default_item);
 
@@ -396,7 +399,8 @@
 }
 
 int stk_agent_display_text(struct stk_agent *agent, const char *text,
-				uint8_t icon_id, ofono_bool_t urgent,
+				const struct stk_icon_id *icon,
+				ofono_bool_t urgent,
 				stk_agent_display_text_cb cb,
 				void *user_data, ofono_destroy_func destroy,
 				int timeout)
@@ -412,7 +416,7 @@
 
 	dbus_message_append_args(agent->msg,
 					DBUS_TYPE_STRING, &text,
-					DBUS_TYPE_BYTE, &icon_id,
+					DBUS_TYPE_BYTE, &icon->id,
 					DBUS_TYPE_BOOLEAN, &priority,
 					DBUS_TYPE_INVALID);
 
@@ -465,8 +469,8 @@
 	CALLBACK_END();
 }
 
-int stk_agent_request_confirmation(struct stk_agent *agent,
-					const char *text, uint8_t icon_id,
+int stk_agent_request_confirmation(struct stk_agent *agent, const char *text,
+					const struct stk_icon_id *icon,
 					stk_agent_confirmation_cb cb,
 					void *user_data,
 					ofono_destroy_func destroy,
@@ -482,7 +486,7 @@
 
 	dbus_message_append_args(agent->msg,
 					DBUS_TYPE_STRING, &text,
-					DBUS_TYPE_BYTE, &icon_id,
+					DBUS_TYPE_BYTE, &icon->id,
 					DBUS_TYPE_INVALID);
 
 	if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
@@ -536,8 +540,8 @@
 	CALLBACK_END();
 }
 
-int stk_agent_request_digit(struct stk_agent *agent,
-				const char *text, uint8_t icon_id,
+int stk_agent_request_digit(struct stk_agent *agent, const char *text,
+				const struct stk_icon_id *icon,
 				stk_agent_string_cb cb, void *user_data,
 				ofono_destroy_func destroy, int timeout)
 {
@@ -551,7 +555,7 @@
 
 	dbus_message_append_args(agent->msg,
 					DBUS_TYPE_STRING, &text,
-					DBUS_TYPE_BYTE, &icon_id,
+					DBUS_TYPE_BYTE, &icon->id,
 					DBUS_TYPE_INVALID);
 
 	if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
@@ -604,7 +608,8 @@
 }
 
 int stk_agent_request_key(struct stk_agent *agent, const char *text,
-				uint8_t icon_id, ofono_bool_t unicode_charset,
+				const struct stk_icon_id *icon,
+				ofono_bool_t unicode_charset,
 				stk_agent_string_cb cb, void *user_data,
 				ofono_destroy_func destroy, int timeout)
 {
@@ -618,7 +623,7 @@
 
 	dbus_message_append_args(agent->msg,
 					DBUS_TYPE_STRING, &text,
-					DBUS_TYPE_BYTE, &icon_id,
+					DBUS_TYPE_BYTE, &icon->id,
 					DBUS_TYPE_INVALID);
 
 	if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
@@ -670,7 +675,8 @@
 }
 
 int stk_agent_request_digits(struct stk_agent *agent, const char *text,
-				uint8_t icon_id, const char *default_text,
+				const struct stk_icon_id *icon,
+				const char *default_text,
 				int min, int max, ofono_bool_t hidden,
 				stk_agent_string_cb cb, void *user_data,
 				ofono_destroy_func destroy, int timeout)
@@ -691,7 +697,7 @@
 
 	dbus_message_append_args(agent->msg,
 					DBUS_TYPE_STRING, &text,
-					DBUS_TYPE_BYTE, &icon_id,
+					DBUS_TYPE_BYTE, &icon->id,
 					DBUS_TYPE_STRING, &default_text,
 					DBUS_TYPE_BYTE, &min_val,
 					DBUS_TYPE_BYTE, &max_val,
@@ -747,7 +753,8 @@
 }
 
 int stk_agent_request_input(struct stk_agent *agent, const char *text,
-				uint8_t icon_id, const char *default_text,
+				const struct stk_icon_id *icon,
+				const char *default_text,
 				ofono_bool_t unicode_charset, int min, int max,
 				ofono_bool_t hidden, stk_agent_string_cb cb,
 				void *user_data, ofono_destroy_func destroy,
@@ -769,7 +776,7 @@
 
 	dbus_message_append_args(agent->msg,
 					DBUS_TYPE_STRING, &text,
-					DBUS_TYPE_BYTE, &icon_id,
+					DBUS_TYPE_BYTE, &icon->id,
 					DBUS_TYPE_STRING, &default_text,
 					DBUS_TYPE_BYTE, &min_val,
 					DBUS_TYPE_BYTE, &max_val,
@@ -824,7 +831,8 @@
 }
 
 int stk_agent_confirm_call(struct stk_agent *agent, const char *text,
-				uint8_t icon_id, stk_agent_confirmation_cb cb,
+				const struct stk_icon_id *icon,
+				stk_agent_confirmation_cb cb,
 				void *user_data, ofono_destroy_func destroy,
 				int timeout)
 {
@@ -838,7 +846,7 @@
 
 	dbus_message_append_args(agent->msg,
 					DBUS_TYPE_STRING, &text,
-					DBUS_TYPE_BYTE, &icon_id,
+					DBUS_TYPE_BYTE, &icon->id,
 					DBUS_TYPE_INVALID);
 
 	if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
@@ -854,3 +862,97 @@
 
 	return 0;
 }
+
+static void play_tone_cb(DBusPendingCall *call, void *data)
+{
+	struct stk_agent *agent = data;
+	stk_agent_tone_cb cb = agent->user_cb;
+	DBusMessage *reply = dbus_pending_call_steal_reply(call);
+	enum stk_agent_result result;
+	gboolean remove_agent;
+
+	if (check_error(agent, reply,
+			ALLOWED_ERROR_TERMINATE, &result) == -EINVAL) {
+		remove_agent = TRUE;
+		goto error;
+	}
+
+	if (dbus_message_get_args(reply, NULL, DBUS_TYPE_INVALID) == FALSE) {
+		ofono_error("Can't parse the reply to PlayTone()");
+		remove_agent = TRUE;
+		goto error;
+	}
+
+	cb(result, agent->user_data);
+	goto done;
+
+	CALLBACK_END();
+}
+
+int stk_agent_play_tone(struct stk_agent *agent, const char *text,
+			const struct stk_icon_id *icon, ofono_bool_t vibrate,
+			const char *tone, stk_agent_tone_cb cb, void *user_data,
+			ofono_destroy_func destroy, int timeout)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+
+	agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+							OFONO_SIM_APP_INTERFACE,
+							"PlayTone");
+	if (agent->msg == NULL)
+		return -ENOMEM;
+
+	dbus_message_append_args(agent->msg,
+					DBUS_TYPE_STRING, &tone,
+					DBUS_TYPE_STRING, &text,
+					DBUS_TYPE_BYTE, &icon->id,
+					DBUS_TYPE_INVALID);
+
+	if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
+						timeout) == FALSE ||
+			agent->call == NULL)
+		return -EIO;
+
+	agent->user_cb = cb;
+	agent->user_data = user_data;
+	agent->user_destroy = destroy;
+
+	dbus_pending_call_set_notify(agent->call, play_tone_cb,
+					agent, NULL);
+
+	return 0;
+}
+
+int stk_agent_loop_tone(struct stk_agent *agent, const char *text,
+			const struct stk_icon_id *icon, ofono_bool_t vibrate,
+			const char *tone, stk_agent_tone_cb cb, void *user_data,
+			ofono_destroy_func destroy, int timeout)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+
+	agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+							OFONO_SIM_APP_INTERFACE,
+							"LoopTone");
+	if (agent->msg == NULL)
+		return -ENOMEM;
+
+	dbus_message_append_args(agent->msg,
+					DBUS_TYPE_STRING, &tone,
+					DBUS_TYPE_STRING, &text,
+					DBUS_TYPE_BYTE, &icon->id,
+					DBUS_TYPE_INVALID);
+
+	if (dbus_connection_send_with_reply(conn, agent->msg, &agent->call,
+						timeout) == FALSE ||
+			agent->call == NULL)
+		return -EIO;
+
+	agent->user_cb = cb;
+	agent->user_data = user_data;
+	agent->user_destroy = destroy;
+
+	dbus_pending_call_set_notify(agent->call, play_tone_cb,
+					agent, NULL);
+
+	return 0;
+}
--- src/stkagent.h
+++ src/stkagent.h
@@ -36,7 +36,7 @@
 
 struct stk_menu {
 	char *title;
-	uint8_t icon_id;
+	struct stk_icon_id icon;
 	struct stk_menu_item *items;
 	int default_item;
 	gboolean soft_key;
@@ -56,6 +56,9 @@
 typedef void (*stk_agent_string_cb)(enum stk_agent_result result,
 					char *string, void *user_data);
 
+typedef void (*stk_agent_tone_cb)(enum stk_agent_result result,
+						void *user_data);
+
 struct stk_agent *stk_agent_new(const char *path, const char *sender,
 					ofono_bool_t remove_on_terminate);
 
@@ -77,45 +80,59 @@
 				int timeout);
 
 int stk_agent_display_text(struct stk_agent *agent, const char *text,
-				uint8_t icon_id, ofono_bool_t urgent,
+				const struct stk_icon_id *icon,
+				ofono_bool_t urgent,
 				stk_agent_display_text_cb cb,
 				void *user_data, ofono_destroy_func destroy,
 				int timeout);
 
-int stk_agent_request_confirmation(struct stk_agent *agent,
-					const char *text, uint8_t icon_id,
+int stk_agent_request_confirmation(struct stk_agent *agent, const char *text,
+					const struct stk_icon_id *icon,
 					stk_agent_confirmation_cb cb,
 					void *user_data,
 					ofono_destroy_func destroy,
 					int timeout);
 
-int stk_agent_request_digit(struct stk_agent *agent,
-				const char *text, uint8_t icon_id,
+int stk_agent_request_digit(struct stk_agent *agent, const char *text,
+				const struct stk_icon_id *icon,
 				stk_agent_string_cb cb, void *user_data,
 				ofono_destroy_func destroy, int timeout);
 
 int stk_agent_request_key(struct stk_agent *agent, const char *text,
-				uint8_t icon_id, ofono_bool_t unicode_charset,
+				const struct stk_icon_id *icon,
+				ofono_bool_t unicode_charset,
 				stk_agent_string_cb cb, void *user_data,
 				ofono_destroy_func destroy, int timeout);
 
 int stk_agent_request_digits(struct stk_agent *agent, const char *text,
-				uint8_t icon_id, const char *default_text,
-				int min, int max, ofono_bool_t hidden,
-				stk_agent_string_cb cb, void *user_data,
-				ofono_destroy_func destroy, int timeout);
+				const struct stk_icon_id *icon,
+				const char *default_text, int min, int max,
+				ofono_bool_t hidden, stk_agent_string_cb cb,
+				void *user_data, ofono_destroy_func destroy,
+				int timeout);
 
 int stk_agent_request_input(struct stk_agent *agent, const char *text,
-				uint8_t icon_id, const char *default_text,
+				const struct stk_icon_id *icon,
+				const char *default_text,
 				ofono_bool_t unicode_charset, int min, int max,
 				ofono_bool_t hidden, stk_agent_string_cb cb,
 				void *user_data, ofono_destroy_func destroy,
 				int timeout);
 
 int stk_agent_confirm_call(struct stk_agent *agent, const char *text,
-				uint8_t icon_id, stk_agent_confirmation_cb cb,
-				void *user_data, ofono_destroy_func destroy,
-				int timeout);
+				const struct stk_icon_id *icon,
+				stk_agent_confirmation_cb cb, void *user_data,
+				ofono_destroy_func destroy, int timeout);
+
+int stk_agent_play_tone(struct stk_agent *agent, const char *text,
+			const struct stk_icon_id *icon, ofono_bool_t vibrate,
+			const char *tone, stk_agent_tone_cb cb, void *user_data,
+			ofono_destroy_func destroy, int timeout);
+
+int stk_agent_loop_tone(struct stk_agent *agent, const char *text,
+			const struct stk_icon_id *icon, ofono_bool_t vibrate,
+			const char *tone, stk_agent_tone_cb cb, void *user_data,
+			ofono_destroy_func destroy, int timeout);
 
 void append_menu_items_variant(DBusMessageIter *iter,
 				const struct stk_menu_item *items);
--- src/stkutil.c
+++ src/stkutil.c
@@ -5455,6 +5455,8 @@
 					&response->select_item.item_id,
 					NULL);
 		break;
+	case STK_COMMAND_TYPE_SEND_SS:
+		break;
 	case STK_COMMAND_TYPE_SETUP_CALL:
 		ok = build_setup_call(&builder, response);
 		break;
--- src/stkutil.h
+++ src/stkutil.h
@@ -254,6 +254,8 @@
 	STK_RESULT_TYPE_COMMAND_NOT_UNDERSTOOD =	0x31,
 	STK_RESULT_TYPE_DATA_NOT_UNDERSTOOD =		0x32,
 	STK_RESULT_TYPE_COMMAND_ID_UNKNOWN =		0x33,
+	STK_RESULT_TYPE_SS_RETURN_ERROR =		0x34,
+	STK_RESULT_TYPE_SMS_RP_ERROR =			0x35,
 	STK_RESULT_TYPE_MINIMUM_NOT_MET =		0x36,
 	STK_RESULT_TYPE_USSD_RETURN_ERROR =		0x37,
 	STK_RESULT_TYPE_CALL_CONTROL_PERMANENT =	0x39,
@@ -263,6 +265,26 @@
 	STK_RESULT_TYPE_MMS_ERROR =			0x3D,
 };
 
+/* Defined according to TS 102.223 Section 8.12.2 */
+enum stk_result_addnl_me_pb {
+	STK_RESULT_ADDNL_ME_PB_NO_SPECIFIC_CAUSE =	0x00,
+	STK_RESULT_ADDNL_ME_PB_SCREEN_BUSY =		0x01,
+	STK_RESULT_ADDNL_ME_PB_BUSY_ON_CALL =		0x02,
+	STK_RESULT_ADDNL_ME_PB_SS_BUSY =		0x03,
+	STK_RESULT_ADDNL_ME_PB_NO_SERVICE =		0x04,
+	STK_RESULT_ADDNL_ME_PB_NO_ACCESS =		0x05,
+	STK_RESULT_ADDNL_ME_PB_NO_RADIO_RESOURCE =	0x06,
+	STK_RESULT_ADDNL_ME_PB_NOT_IN_SPEECH_CALL =	0x07,
+	STK_RESULT_ADDNL_ME_PB_USSD_BUSY =		0x08,
+	STK_RESULT_ADDNL_ME_PB_BUSY_ON_SEND_DTMF =	0x09,
+	STK_RESULT_ADDNL_ME_PB_NO_NAA_ACTIVE =		0x0A
+};
+
+/* Defined according to TS 31.111 Section 8.12.4 */
+enum stk_result_addnl_ss_pb {
+	STK_RESULT_ADDNL_SS_PB_NO_SPECIFIC_CAUSE =	0x00
+};
+
 enum stk_tone_type {
 	STK_TONE_TYPE_DIAL_TONE =	0x01,
 	STK_TONE_TYPE_BUSY_TONE =	0x02,
--- src/ussd.c
+++ src/ussd.c
@@ -545,6 +545,9 @@
 					void *data)
 {
 	struct ofono_ussd *ussd = data;
+	struct ofono_modem *modem = __ofono_atom_get_modem(ussd->atom);
+	struct ofono_atom *vca;
+	gboolean call_in_progress;
 	const char *str;
 	int dcs = 0x0f;
 	unsigned char buf[160];
@@ -564,8 +567,17 @@
 	if (recognized_control_string(ussd, str, msg))
 		return NULL;
 
+	vca = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_VOICECALL);
+
+	if (vca)
+		call_in_progress =
+			__ofono_voicecall_is_busy(__ofono_atom_get_data(vca),
+					OFONO_VOICECALL_INTERACTION_NONE);
+	else
+		call_in_progress = FALSE;
+
 	DBG("No.., checking if this is a USSD string");
-	if (!valid_ussd_string(str))
+	if (!valid_ussd_string(str, call_in_progress))
 		return __ofono_error_invalid_format(msg);
 
 	if (!ussd_encode(str, &num_packed, buf))
@@ -888,6 +900,9 @@
 		return -EBUSY;
 
 	req = g_try_new0(struct ussd_request, 1);
+	if (req == NULL)
+		return -ENOMEM;
+
 	req->cb = cb;
 	req->user_data = user_data;
 
--- src/voicecall.c
+++ src/voicecall.c
@@ -56,6 +56,8 @@
 	void *driver_data;
 	struct ofono_atom *atom;
 	struct dial_request *dial_req;
+	GQueue *toneq;
+	guint tone_source;
 };
 
 struct voicecall {
@@ -79,12 +81,22 @@
 	struct ofono_phone_number ph;
 };
 
+struct tone_queue_entry {
+	char *tone_str;
+	char *left;
+	ofono_voicecall_tone_cb_t cb;
+	void *user_data;
+	ofono_destroy_func destroy;
+	int id;
+};
+
 static const char *default_en_list[] = { "911", "112", NULL };
 static const char *default_en_list_no_sim[] = { "119", "118", "999", "110",
 						"08", "000", NULL };
 
 static void generic_callback(const struct ofono_error *error, void *data);
 static void multirelease_callback(const struct ofono_error *err, void *data);
+static gboolean tone_request_run(gpointer user_data);
 
 static gint call_compare_by_id(gconstpointer a, gconstpointer b)
 {
@@ -213,11 +225,11 @@
 	return r;
 }
 
-static void dial_request_finish(struct ofono_voicecall *vc, gboolean callback)
+static void dial_request_finish(struct ofono_voicecall *vc)
 {
 	struct dial_request *dial_req = vc->dial_req;
 
-	if (callback && dial_req->cb)
+	if (dial_req->cb)
 		dial_req->cb(dial_req->call ? dial_req->call->call : NULL,
 				dial_req->user_data);
 
@@ -226,6 +238,83 @@
 	vc->dial_req = NULL;
 }
 
+static gboolean voicecalls_can_dtmf(struct ofono_voicecall *vc)
+{
+	GSList *l;
+	struct voicecall *v;
+
+	for (l = vc->call_list; l; l = l->next) {
+		v = l->data;
+
+		if (v->call->status == CALL_STATUS_ACTIVE)
+			return TRUE;
+
+		/* Connected for 2nd stage dialing */
+		if (v->call->status == CALL_STATUS_ALERTING)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static int tone_queue(struct ofono_voicecall *vc, const char *tone_str,
+			ofono_voicecall_tone_cb_t cb, void *data,
+			ofono_destroy_func destroy)
+{
+	struct tone_queue_entry *entry;
+	int id = 1;
+	int n = 0;
+	int i;
+
+	/*
+	 * Tones can be 0-9, *, #, A-D according to 27.007 C.2.11,
+	 * and p for Pause.
+	 */
+	for (i = 0; tone_str[i]; i++)
+		if (!g_ascii_isdigit(tone_str[i]) && tone_str[i] != 'p' &&
+				tone_str[i] != '*' && tone_str[i] != '#' &&
+				(tone_str[i] < 'A' || tone_str[i] > 'D'))
+			return -EINVAL;
+
+	while ((entry = g_queue_peek_nth(vc->toneq, n++)) != NULL)
+		if (entry->id >= id)
+			id = entry->id + 1;
+
+	entry = g_try_new0(struct tone_queue_entry, 1);
+	if (entry == NULL)
+		return -ENOMEM;
+
+	entry->tone_str = g_strdup(tone_str);
+	entry->left = entry->tone_str;
+	entry->cb = cb;
+	entry->user_data = data;
+	entry->destroy = destroy;
+	entry->id = id;
+
+	g_queue_push_tail(vc->toneq, entry);
+
+	if (g_queue_get_length(vc->toneq) == 1)
+		g_timeout_add(0, tone_request_run, vc);
+
+	return id;
+}
+
+static void tone_request_finish(struct ofono_voicecall *vc,
+				struct tone_queue_entry *entry,
+				int error, gboolean callback)
+{
+	g_queue_remove(vc->toneq, entry);
+
+	if (callback)
+		entry->cb(error, entry->user_data);
+
+	if (entry->destroy)
+		entry->destroy(entry->user_data);
+
+	g_free(entry->tone_str);
+	g_free(entry);
+}
+
 static void append_voicecall_properties(struct voicecall *v,
 					DBusMessageIter *dict)
 {
@@ -338,7 +427,7 @@
 		return;
 
 	if (!call || call == vc->dial_req->call)
-		dial_request_finish(vc, TRUE);
+		dial_request_finish(vc);
 }
 
 static DBusMessage *voicecall_hangup(DBusConnection *conn,
@@ -577,12 +666,19 @@
 						&timestr);
 
 		if (call->vc->dial_req && call == call->vc->dial_req->call)
-			dial_request_finish(call->vc, TRUE);
+			dial_request_finish(call->vc);
 	}
 
 	if (status == CALL_STATUS_DISCONNECTED && call->vc->dial_req &&
 			call == call->vc->dial_req->call)
-		dial_request_finish(call->vc, TRUE);
+		dial_request_finish(call->vc);
+
+	if (!voicecalls_can_dtmf(call->vc)) {
+		struct tone_queue_entry *entry;
+
+		while ((entry = g_queue_peek_head(call->vc->toneq)))
+			tone_request_finish(call->vc, entry, ENOENT, TRUE);
+	}
 }
 
 static void voicecall_set_call_lineid(struct voicecall *v,
@@ -699,25 +795,6 @@
 	return FALSE;
 }
 
-static gboolean voicecalls_can_dtmf(struct ofono_voicecall *vc)
-{
-	GSList *l;
-	struct voicecall *v;
-
-	for (l = vc->call_list; l; l = l->next) {
-		v = l->data;
-
-		if (v->call->status == CALL_STATUS_ACTIVE)
-			return TRUE;
-
-		/* Connected for 2nd stage dialing */
-		if (v->call->status == CALL_STATUS_ALERTING)
-			return TRUE;
-	}
-
-	return FALSE;
-}
-
 static gboolean voicecalls_have_with_status(struct ofono_voicecall *vc, int status)
 {
 	GSList *l;
@@ -1527,13 +1604,26 @@
 	return NULL;
 }
 
+static void tone_callback(int error, void *data)
+{
+	struct ofono_voicecall *vc = data;
+	DBusMessage *reply;
+
+	if (error)
+		reply = __ofono_error_failed(vc->pending);
+	else
+		reply = dbus_message_new_method_return(vc->pending);
+
+	__ofono_dbus_pending_reply(&vc->pending, reply);
+}
+
 static DBusMessage *manager_tone(DBusConnection *conn,
 					DBusMessage *msg, void *data)
 {
 	struct ofono_voicecall *vc = data;
 	const char *in_tones;
 	char *tones;
-	int i, len;
+	int err, len;
 
 	if (vc->pending)
 		return __ofono_error_busy(msg);
@@ -1556,23 +1646,15 @@
 
 	tones = g_ascii_strup(in_tones, len);
 
-	/* Tones can be 0-9, *, #, A-D according to 27.007 C.2.11 */
-	for (i = 0; i < len; i++) {
-		if (g_ascii_isdigit(tones[i]) ||
-			tones[i] == '*' || tones[i] == '#' ||
-				(tones[i] >= 'A' && tones[i] <= 'D'))
-			continue;
+	err = tone_queue(vc, tones, tone_callback, vc, NULL);
 
-		g_free(tones);
+	g_free(tones);
+
+	if (err < 0)
 		return __ofono_error_invalid_format(msg);
-	}
 
 	vc->pending = dbus_message_ref(msg);
 
-	vc->driver->send_tones(vc, tones, generic_callback, vc);
-
-	g_free(tones);
-
 	return NULL;
 }
 
@@ -1970,7 +2052,7 @@
 	}
 
 	if (vc->dial_req)
-		dial_request_finish(vc, TRUE);
+		dial_request_finish(vc);
 
 	for (l = vc->call_list; l; l = l->next)
 		voicecall_dbus_unregister(vc, l->data);
@@ -2012,6 +2094,20 @@
 		vc->sim = NULL;
 	}
 
+	if (vc->tone_source) {
+		g_source_remove(vc->tone_source);
+		vc->tone_source = 0;
+	}
+
+	if (vc->toneq) {
+		struct tone_queue_entry *entry;
+
+		while ((entry = g_queue_peek_head(vc->toneq)))
+			tone_request_finish(vc, entry, ESHUTDOWN, TRUE);
+
+		g_queue_free(vc->toneq);
+	}
+
 	g_free(vc);
 }
 
@@ -2031,6 +2127,8 @@
 	if (vc == NULL)
 		return NULL;
 
+	vc->toneq = g_queue_new();
+
 	vc->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_VOICECALL,
 						voicecall_remove, vc);
 
@@ -2171,7 +2269,7 @@
 ofono_bool_t __ofono_voicecall_is_busy(struct ofono_voicecall *vc,
 					enum ofono_voicecall_interaction type)
 {
-	if (vc->pending)
+	if (vc->pending || vc->dial_req)
 		return TRUE;
 
 	switch (type) {
@@ -2207,7 +2305,7 @@
 				&need_to_emit);
 
 	if (v == NULL) {
-		dial_request_finish(vc, TRUE);
+		dial_request_finish(vc);
 		return;
 	}
 
@@ -2225,7 +2323,7 @@
 	v->untracked = TRUE;
 
 	if (v->call->status == CALL_STATUS_ACTIVE)
-		dial_request_finish(vc, TRUE);
+		dial_request_finish(vc);
 
 	if (need_to_emit)
 		voicecalls_emit_call_added(vc, v);
@@ -2242,7 +2340,7 @@
 	struct ofono_voicecall *vc = data;
 
 	if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
-		dial_request_finish(vc, TRUE);
+		dial_request_finish(vc);
 		return;
 	}
 
@@ -2272,7 +2370,7 @@
 			vc->driver->release_all_active == NULL)
 		return -ENOSYS;
 
-	if (vc->dial_req || vc->pending)
+	if (__ofono_voicecall_is_busy(vc, interaction) == TRUE)
 		return -EBUSY;
 
 	/*
@@ -2281,6 +2379,9 @@
 	 */
 
 	req = g_try_new0(struct dial_request, 1);
+	if (req == NULL)
+		return -ENOMEM;
+
 	req->message = g_strdup(message);
 	req->icon_id = icon_id;
 	req->interaction = interaction;
@@ -2293,11 +2394,6 @@
 
 	vc->dial_req = req;
 
-	if (__ofono_voicecall_is_busy(vc, interaction) == TRUE) {
-		dial_request_finish(vc, FALSE);
-		return -EBUSY;
-	}
-
 	switch (interaction) {
 	case OFONO_VOICECALL_INTERACTION_NONE:
 		dial_request(vc);
@@ -2328,3 +2424,110 @@
 
 	vc->dial_req->cb = NULL;
 }
+
+static void tone_request_cb(const struct ofono_error *error, void *data)
+{
+	struct ofono_voicecall *vc = data;
+	struct tone_queue_entry *entry = g_queue_peek_head(vc->toneq);
+	int len = 0;
+
+	if (!entry)
+		return;
+
+	/*
+	 * Call back with error only if the error is related to the
+	 * current entry.  If the error corresponds to a cancelled
+	 * request, do nothing.
+	 */
+	if (error && error->type != OFONO_ERROR_TYPE_NO_ERROR &&
+			entry->left > entry->tone_str) {
+		DBG("command failed with error: %s",
+				telephony_error_to_str(error));
+
+		tone_request_finish(vc, entry, EIO, TRUE);
+
+		goto done;
+	}
+
+	if (*entry->left == '\0') {
+		tone_request_finish(vc, entry, 0, TRUE);
+
+		goto done;
+	}
+
+	len = strspn(entry->left, "pP");
+	entry->left += len;
+
+done:
+	/*
+	 * Wait 3 seconds per PAUSE, same as for DTMF separator characters
+	 * passed in a telephone number according to TS 22.101 A.21,
+	 * although 27.007 claims this delay can be set using S8 and
+	 * defaults to 2 seconds.
+	 */
+	vc->tone_source = g_timeout_add_seconds(len * 3, tone_request_run, vc);
+}
+
+static gboolean tone_request_run(gpointer user_data)
+{
+	struct ofono_voicecall *vc = user_data;
+	struct tone_queue_entry *entry = g_queue_peek_head(vc->toneq);
+	char buf[256];
+	unsigned len;
+
+	vc->tone_source = 0;
+
+	if (!entry)
+		return FALSE;
+
+	len = strcspn(entry->left, "pP");
+
+	if (len) {
+		if (len >= sizeof(buf))
+			len = sizeof(buf) - 1;
+
+		memcpy(buf, entry->left, len);
+		buf[len] = '\0';
+		entry->left += len;
+
+		vc->driver->send_tones(vc, buf, tone_request_cb, vc);
+	} else
+		tone_request_cb(NULL, vc);
+
+	return FALSE;
+}
+
+int __ofono_voicecall_tone_send(struct ofono_voicecall *vc,
+				const char *tone_str,
+				ofono_voicecall_tone_cb_t cb, void *user_data)
+{
+	if (!vc->driver->send_tones)
+		return -ENOSYS;
+
+	/* Send DTMFs only if we have at least one connected call */
+	if (!voicecalls_can_dtmf(vc))
+		return -ENOENT;
+
+	return tone_queue(vc, tone_str, cb, user_data, NULL);
+}
+
+void __ofono_voicecall_tone_cancel(struct ofono_voicecall *vc, int id)
+{
+	struct tone_queue_entry *entry;
+	int n = 0;
+
+	while ((entry = g_queue_peek_nth(vc->toneq, n++)) != NULL)
+		if (entry->id == id)
+			break;
+
+	tone_request_finish(vc, entry, 0, FALSE);
+
+	/*
+	 * If we were in the middle of a PAUSE, wake queue up
+	 * now, else wake up when current tone finishes.
+	 */
+	if (n == 1 && vc->tone_source) {
+		g_source_remove(vc->tone_source);
+		tone_request_run(vc);
+	}
+}
--- test/activate-context
+++ test/activate-context
@@ -25,7 +25,10 @@
 
 	connman.SetProperty("Powered", dbus.Boolean(1))
 
-	path = contexts[0][0]
+	if len(sys.argv) > 1:
+		path = contexts[int(sys.argv[1])][0]
+	else:
+		path = contexts[0][0]
 
 	context = dbus.Interface(bus.get_object('org.ofono', path),
 					'org.ofono.ConnectionContext')
--- test/deactivate-context
+++ test/deactivate-context
@@ -23,7 +23,10 @@
 		print "No context available"
 		sys.exit(1)
 
-	path = contexts[0][0]
+	if len(sys.argv) > 1:
+		path = contexts[int(sys.argv[1])][0]
+	else:
+		path = contexts[0][0]
 
 	context = dbus.Interface(bus.get_object('org.ofono', path),
 					'org.ofono.ConnectionContext')
--- test/get-icon
+++ test/get-icon
+#!/usr/bin/python
+
+import dbus
+import sys
+
+bus = dbus.SystemBus()
+
+if len(sys.argv) == 2:
+	id = sys.argv[1]
+else:
+	print "%s <icon id>" % (sys.argv[0])
+	sys.exit(0)
+
+manager = dbus.Interface(bus.get_object("org.ofono", "/"),
+							"org.ofono.Manager")
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+	if "org.ofono.SimManager" not in properties["Interfaces"]:
+		continue
+
+sim = dbus.Interface(bus.get_object('org.ofono', path),
+				'org.ofono.SimManager')
+
+icon = sim.GetIcon(dbus.Byte(int(sys.argv[1])))
+
+xpm = ""
+for byte in icon:
+	xpm += str(byte)
+print xpm
--- test/set-roaming-allowed
+++ test/set-roaming-allowed
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+						'org.ofono.Manager')
+
+modems = manager.GetModems()
+
+for path, properties in modems:
+	if "org.ofono.ConnectionManager" not in properties["Interfaces"]:
+		continue
+
+	connman = dbus.Interface(bus.get_object('org.ofono', path),
+					'org.ofono.ConnectionManager')
+
+	if len(sys.argv) > 1:
+		allowed = dbus.Boolean(int(sys.argv[1]))
+	else:
+		allowed = dbus.Boolean(1)
+
+	connman.SetProperty("RoamingAllowed", allowed)
+
+	print "Setting %s to RoamingAllowed=%d" % (path, allowed)
--- test/set-use-sms-reports
+++ test/set-use-sms-reports
@@ -11,11 +11,11 @@
 elif len(sys.argv) == 2:
 	manager = dbus.Interface(bus.get_object('org.ofono', '/'),
 						'org.ofono.Manager')
-	modems = manager.getmodems()
+	modems = manager.GetModems()
 	path = modems[0][0]
 	enabled = sys.argv[1]
 else:
-	print "%s [PATH] topics" % (sys.argv[0])
+	print "%s [PATH] on/off" % (sys.argv[0])
 	sys.exit(1)
 
 print "Setting delivery report use for modem %s..." % path

++++++ ofono.yaml
--- ofono.yaml
+++ ofono.yaml
@@ -1,6 +1,6 @@
 Name: ofono
 Summary: Open Source Telephony
-Version: 0.31
+Version: 0.33
 Release: 1
 Group: System/Networking
 License: GPLv2



More information about the MeeGo-commits mailing list