aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGuido Günther <agx@sigxcpu.org>2014-02-05 08:40:16 +0100
committerGuido Günther <agx@sigxcpu.org>2014-02-05 08:40:16 +0100
commitfa2b467e288cb137ffd792becbf0c1e757d85be4 (patch)
treece308eb0886e93805e7d88bccce48c93797fd6dd /src
parentafc4b839a31c530d73b91aa2483795f185eb7e52 (diff)
New upstream version 1.2.0upstream/1.2.0upstream
Diffstat (limited to 'src')
-rw-r--r--src/77-mm-pcmcia-device-blacklist.rules3
-rw-r--r--src/77-mm-platform-serial-whitelist.rules3
-rw-r--r--src/77-mm-usb-device-blacklist.rules32
-rw-r--r--src/77-mm-usb-serial-adapters-greylist.rules3
-rw-r--r--src/Makefile.am30
-rw-r--r--src/Makefile.in184
-rw-r--r--src/mm-at-serial-port.c28
-rw-r--r--src/mm-at-serial-port.h4
-rw-r--r--src/mm-base-modem.c7
-rw-r--r--src/mm-bearer-list.c27
-rw-r--r--src/mm-bearer-list.h1
-rw-r--r--src/mm-bearer-mbim.c166
-rw-r--r--src/mm-bearer-qmi.c20
-rw-r--r--src/mm-bearer.c30
-rw-r--r--src/mm-bearer.h16
-rw-r--r--src/mm-broadband-bearer.c16
-rw-r--r--src/mm-broadband-modem-mbim.c96
-rw-r--r--src/mm-broadband-modem-qmi.c1780
-rw-r--r--src/mm-broadband-modem.c231
-rw-r--r--src/mm-error-helpers.c14
-rw-r--r--src/mm-iface-modem-3gpp.c149
-rw-r--r--src/mm-iface-modem-3gpp.h28
-rw-r--r--src/mm-iface-modem-cdma.c26
-rw-r--r--src/mm-iface-modem-cdma.h2
-rw-r--r--src/mm-iface-modem-messaging.c33
-rw-r--r--src/mm-iface-modem-oma.c1265
-rw-r--r--src/mm-iface-modem-oma.h164
-rw-r--r--src/mm-iface-modem-signal.c612
-rw-r--r--src/mm-iface-modem-signal.h95
-rw-r--r--src/mm-iface-modem.c374
-rw-r--r--src/mm-iface-modem.h7
-rw-r--r--src/mm-marshal.list3
-rw-r--r--src/mm-modem-helpers-mbim.c48
-rw-r--r--src/mm-modem-helpers-mbim.h4
-rw-r--r--src/mm-modem-helpers-qmi.c103
-rw-r--r--src/mm-modem-helpers-qmi.h10
-rw-r--r--src/mm-modem-helpers.c152
-rw-r--r--src/mm-modem-helpers.h2
-rw-r--r--src/mm-plugin.c16
-rw-r--r--src/mm-port-probe.c14
-rw-r--r--src/mm-port.c25
-rw-r--r--src/mm-port.h11
-rw-r--r--src/mm-qmi-port.c6
-rw-r--r--src/mm-serial-port.c73
-rw-r--r--src/mm-sim-mbim.c152
-rw-r--r--src/mm-sim.c78
-rw-r--r--src/mm-sms-list.c8
-rw-r--r--src/mm-sms-mbim.c5
-rw-r--r--src/mm-sms-part-3gpp.c1169
-rw-r--r--src/mm-sms-part-3gpp.h54
-rw-r--r--src/mm-sms-part-cdma.c1616
-rw-r--r--src/mm-sms-part-cdma.h37
-rw-r--r--src/mm-sms-part.c1135
-rw-r--r--src/mm-sms-part.h39
-rw-r--r--src/mm-sms-qmi.c129
-rw-r--r--src/mm-sms.c138
-rw-r--r--src/tests/Makefile.am39
-rw-r--r--src/tests/Makefile.in120
-rw-r--r--src/tests/test-at-serial-port.c10
-rw-r--r--src/tests/test-modem-helpers.c219
-rw-r--r--src/tests/test-qcdm-serial-port.c10
-rw-r--r--src/tests/test-sms-part-3gpp.c (renamed from src/tests/test-sms-part.c)86
-rw-r--r--src/tests/test-sms-part-cdma.c547
63 files changed, 9558 insertions, 1946 deletions
diff --git a/src/77-mm-pcmcia-device-blacklist.rules b/src/77-mm-pcmcia-device-blacklist.rules
index 76259a2..f0f8474 100644
--- a/src/77-mm-pcmcia-device-blacklist.rules
+++ b/src/77-mm-pcmcia-device-blacklist.rules
@@ -1,10 +1,9 @@
# do not edit this file, it will be overwritten on update
-ACTION!="add|change", GOTO="mm_pcmcia_device_blacklist_end"
+ACTION!="add|change|move", GOTO="mm_pcmcia_device_blacklist_end"
SUBSYSTEM!="pcmcia", GOTO="mm_pcmcia_device_blacklist_end"
# Gemplus Serial Port smartcard adapter
ATTRS{prod_id1}=="Gemplus", ATTRS{prod_id2}=="SerialPort", ATTRS{prod_id3}=="GemPC Card", ENV{ID_MM_DEVICE_IGNORE}="1"
LABEL="mm_pcmcia_device_blacklist_end"
-
diff --git a/src/77-mm-platform-serial-whitelist.rules b/src/77-mm-platform-serial-whitelist.rules
index b62d0a6..faf4472 100644
--- a/src/77-mm-platform-serial-whitelist.rules
+++ b/src/77-mm-platform-serial-whitelist.rules
@@ -1,6 +1,6 @@
# do not edit this file, it will be overwritten on update
-ACTION!="add|change", GOTO="mm_platform_device_whitelist_end"
+ACTION!="add|change|move", GOTO="mm_platform_device_whitelist_end"
SUBSYSTEM!="platform", GOTO="mm_platform_device_whitelist_end"
# Be careful here since many devices connected to platform drivers on PCs
@@ -11,4 +11,3 @@ SUBSYSTEM!="platform", GOTO="mm_platform_device_whitelist_end"
DRIVERS=="atmel_usart", ENV{ID_MM_PLATFORM_DRIVER_PROBE}="1"
LABEL="mm_platform_device_whitelist_end"
-
diff --git a/src/77-mm-usb-device-blacklist.rules b/src/77-mm-usb-device-blacklist.rules
index 30355b1..044d80f 100644
--- a/src/77-mm-usb-device-blacklist.rules
+++ b/src/77-mm-usb-device-blacklist.rules
@@ -1,6 +1,6 @@
# do not edit this file, it will be overwritten on update
-ACTION!="add|change", GOTO="mm_usb_device_blacklist_end"
+ACTION!="add|change|move", GOTO="mm_usb_device_blacklist_end"
SUBSYSTEM!="usb", GOTO="mm_usb_device_blacklist_end"
ENV{DEVTYPE}!="usb_device", GOTO="mm_usb_device_blacklist_end"
@@ -69,7 +69,7 @@ ATTRS{idVendor}=="9e88", ATTRS{idProduct}=="9e8f", ENV{ID_MM_DEVICE_IGNORE}="1"
ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="6124", ENV{ID_MM_DEVICE_IGNORE}="1"
# Dangerous Prototypes Bus Pirate v4
-ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fb00" ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="fb00", ENV{ID_MM_DEVICE_IGNORE}="1"
# All devices from the Swiss Federal Institute of Technology
ATTRS{idVendor}=="0617", ENV{ID_MM_DEVICE_IGNORE}="1"
@@ -84,7 +84,33 @@ ATTRS{idVendor}=="2341", ENV{ID_MM_DEVICE_IGNORE}="1"
ATTRS{idVendor}=="1b4f", ATTRS{idProduct}=="9207", ENV{ID_MM_DEVICE_IGNORE}="1"
ATTRS{idVendor}=="1b4f", ATTRS{idProduct}=="9208", ENV{ID_MM_DEVICE_IGNORE}="1"
+# Adafruit Flora
+ATTRS{idVendor}=="239a", ATTRS{idProduct}=="0004", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="239a", ATTRS{idProduct}=="8004", ENV{ID_MM_DEVICE_IGNORE}="1"
+
# Altair U-Boot device
-ATTRS{idVendor}=="0216", ATTRS{idProduct}=="0051" ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="0216", ATTRS{idProduct}=="0051", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Bluegiga BLE112B
+ATTRS{idVendor}=="2458", ATTRS{idProduct}=="0001", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# MediaTek GPS chip (HOLUX M-1200E, GlobalTop Gms-d1, etc)
+ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="3329", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# PS-360 OEM (GPS sold with MS Street and Trips 2005)
+ATTRS{idVendor}=="067b", ATTRS{idProduct}=="aaa0", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# u-blox AG, u-blox 5 GPS chips
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a5", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a6", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Garmin GPS devices
+DRIVERS=="garmin_gps", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Cypress M8-based GPS devices, UPSes, and serial converters
+DRIVERS=="cypress_m8", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# All devices in the Openmoko vendor ID
+ATTRS{idVendor}=="1d50", ENV{ID_MM_DEVICE_IGNORE}="1"
LABEL="mm_usb_device_blacklist_end"
diff --git a/src/77-mm-usb-serial-adapters-greylist.rules b/src/77-mm-usb-serial-adapters-greylist.rules
index 1f30833..3f3e090 100644
--- a/src/77-mm-usb-serial-adapters-greylist.rules
+++ b/src/77-mm-usb-serial-adapters-greylist.rules
@@ -1,6 +1,6 @@
# do not edit this file, it will be overwritten on update
-ACTION!="add|change", GOTO="mm_usb_serial_adapters_greylist_end"
+ACTION!="add|change|move", GOTO="mm_usb_serial_adapters_greylist_end"
SUBSYSTEM!="usb", GOTO="mm_usb_serial_adapters_greylist_end"
ENV{DEVTYPE}!="usb_device", GOTO="mm_usb_serial_adapters_greylist_end"
@@ -24,6 +24,7 @@ ATTRS{idVendor}=="0711", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
# Cygnal Integrated Products, Inc. CP210x
ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
+ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea71", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
# QinHeng Electronics HL-340
ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
diff --git a/src/Makefile.am b/src/Makefile.am
index 4531a99..2051472 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -30,7 +30,11 @@ libmodem_helpers_la_SOURCES = \
mm-charsets.c \
mm-charsets.h \
mm-sms-part.h \
- mm-sms-part.c
+ mm-sms-part.c \
+ mm-sms-part-3gpp.h \
+ mm-sms-part-3gpp.c \
+ mm-sms-part-cdma.h \
+ mm-sms-part-cdma.c
# Additional QMI support in libmodem-helpers
if WITH_QMI
@@ -125,15 +129,6 @@ mm-daemon-enums-types.c: Makefile.am $(top_srcdir)/build-aux/mm-enums-template.c
--template $(top_srcdir)/build-aux/mm-enums-template.c \
$(DAEMON_ENUMS) > $@
-# Marshallers
-
-mm-marshal.h: mm-marshal.list
- $(AM_V_GEN) $(GLIB_GENMARSHAL) $< --prefix=mm_marshal --header > $@
-
-mm-marshal.c: mm-marshal.list mm-marshal.h
- $(AM_V_GEN) echo "#include \"mm-marshal.h\"" > $@ && \
- $(GLIB_GENMARSHAL) $< --prefix=mm_marshal --body >> $@
-
sbin_PROGRAMS = ModemManager
ModemManager_CPPFLAGS = \
@@ -156,8 +151,6 @@ ModemManager_LDADD = \
$(top_builddir)/libqcdm/src/libqcdm.la
nodist_ModemManager_SOURCES = \
- mm-marshal.h \
- mm-marshal.c \
mm-daemon-enums-types.h \
mm-daemon-enums-types.c
@@ -191,8 +184,6 @@ ModemManager_SOURCES = \
mm-base-modem-at.c \
mm-base-modem.h \
mm-base-modem.c \
- mm-sms-part.h \
- mm-sms-part.c \
mm-sms.h \
mm-sms.c \
mm-sms-list.h \
@@ -215,6 +206,10 @@ ModemManager_SOURCES = \
mm-iface-modem-time.c \
mm-iface-modem-firmware.h \
mm-iface-modem-firmware.c \
+ mm-iface-modem-signal.h \
+ mm-iface-modem-signal.c \
+ mm-iface-modem-oma.h \
+ mm-iface-modem-oma.c \
mm-broadband-modem.h \
mm-broadband-modem.c \
mm-serial-parsers.c \
@@ -228,8 +223,6 @@ ModemManager_SOURCES = \
# Additional dependency rules
mm-bearer.c: mm-daemon-enums-types.h
-mm-sms-list.c: mm-marshal.h
-mm-sim.c: mm-marshal.h
# Additional Polkit support
if WITH_POLKIT
@@ -271,12 +264,9 @@ ModemManager_LDADD += $(MBIM_LIBS)
endif
EXTRA_DIST = \
- $(udevrules_DATA) \
- mm-marshal.list
+ $(udevrules_DATA)
CLEANFILES = \
- mm-marshal.h \
- mm-marshal.c \
mm-daemon-enums-types.h \
mm-daemon-enums-types.c \
mm-serial-enums-types.h \
diff --git a/src/Makefile.in b/src/Makefile.in
index a08e3db..306ccda 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2013 Free Software Foundation, Inc.
@@ -152,12 +152,13 @@ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
$(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/gtk-doc.m4 \
$(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \
- $(top_srcdir)/m4/intltool.m4 $(top_srcdir)/m4/lib-ld.m4 \
- $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
- $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
- $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
- $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
- $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/m4/intltool.m4 $(top_srcdir)/m4/introspection.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/vapigen.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
@@ -171,8 +172,10 @@ libmodem_helpers_la_DEPENDENCIES = \
am__libmodem_helpers_la_SOURCES_DIST = mm-error-helpers.c \
mm-error-helpers.h mm-modem-helpers.c mm-modem-helpers.h \
mm-charsets.c mm-charsets.h mm-sms-part.h mm-sms-part.c \
- mm-modem-helpers-qmi.c mm-modem-helpers-qmi.h \
- mm-modem-helpers-mbim.c mm-modem-helpers-mbim.h
+ mm-sms-part-3gpp.h mm-sms-part-3gpp.c mm-sms-part-cdma.h \
+ mm-sms-part-cdma.c mm-modem-helpers-qmi.c \
+ mm-modem-helpers-qmi.h mm-modem-helpers-mbim.c \
+ mm-modem-helpers-mbim.h
@WITH_QMI_TRUE@am__objects_1 = \
@WITH_QMI_TRUE@ libmodem_helpers_la-mm-modem-helpers-qmi.lo
@WITH_MBIM_TRUE@am__objects_2 = \
@@ -181,7 +184,9 @@ am_libmodem_helpers_la_OBJECTS = \
libmodem_helpers_la-mm-error-helpers.lo \
libmodem_helpers_la-mm-modem-helpers.lo \
libmodem_helpers_la-mm-charsets.lo \
- libmodem_helpers_la-mm-sms-part.lo $(am__objects_1) \
+ libmodem_helpers_la-mm-sms-part.lo \
+ libmodem_helpers_la-mm-sms-part-3gpp.lo \
+ libmodem_helpers_la-mm-sms-part-cdma.lo $(am__objects_1) \
$(am__objects_2)
libmodem_helpers_la_OBJECTS = $(am_libmodem_helpers_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
@@ -215,9 +220,9 @@ am__ModemManager_SOURCES_DIST = main.c mm-context.h mm-context.c \
mm-plugin-manager.h mm-sim.h mm-sim.c mm-bearer.h mm-bearer.c \
mm-broadband-bearer.h mm-broadband-bearer.c mm-bearer-list.h \
mm-bearer-list.c mm-base-modem-at.h mm-base-modem-at.c \
- mm-base-modem.h mm-base-modem.c mm-sms-part.h mm-sms-part.c \
- mm-sms.h mm-sms.c mm-sms-list.h mm-sms-list.c mm-iface-modem.h \
- mm-iface-modem.c mm-iface-modem-3gpp.h mm-iface-modem-3gpp.c \
+ mm-base-modem.h mm-base-modem.c mm-sms.h mm-sms.c \
+ mm-sms-list.h mm-sms-list.c mm-iface-modem.h mm-iface-modem.c \
+ mm-iface-modem-3gpp.h mm-iface-modem-3gpp.c \
mm-iface-modem-3gpp-ussd.h mm-iface-modem-3gpp-ussd.c \
mm-iface-modem-cdma.h mm-iface-modem-cdma.c \
mm-iface-modem-simple.h mm-iface-modem-simple.c \
@@ -225,9 +230,11 @@ am__ModemManager_SOURCES_DIST = main.c mm-context.h mm-context.c \
mm-iface-modem-messaging.h mm-iface-modem-messaging.c \
mm-iface-modem-time.h mm-iface-modem-time.c \
mm-iface-modem-firmware.h mm-iface-modem-firmware.c \
- mm-broadband-modem.h mm-broadband-modem.c mm-serial-parsers.c \
- mm-serial-parsers.h mm-port-probe.h mm-port-probe.c \
- mm-port-probe-at.h mm-port-probe-at.c mm-plugin.c mm-plugin.h \
+ mm-iface-modem-signal.h mm-iface-modem-signal.c \
+ mm-iface-modem-oma.h mm-iface-modem-oma.c mm-broadband-modem.h \
+ mm-broadband-modem.c mm-serial-parsers.c mm-serial-parsers.h \
+ mm-port-probe.h mm-port-probe.c mm-port-probe-at.h \
+ mm-port-probe-at.c mm-plugin.c mm-plugin.h \
mm-auth-provider-polkit.c mm-auth-provider-polkit.h \
mm-sms-qmi.h mm-sms-qmi.c mm-sim-qmi.h mm-sim-qmi.c \
mm-bearer-qmi.h mm-bearer-qmi.c mm-broadband-modem-qmi.h \
@@ -257,7 +264,6 @@ am_ModemManager_OBJECTS = ModemManager-main.$(OBJEXT) \
ModemManager-mm-bearer-list.$(OBJEXT) \
ModemManager-mm-base-modem-at.$(OBJEXT) \
ModemManager-mm-base-modem.$(OBJEXT) \
- ModemManager-mm-sms-part.$(OBJEXT) \
ModemManager-mm-sms.$(OBJEXT) \
ModemManager-mm-sms-list.$(OBJEXT) \
ModemManager-mm-iface-modem.$(OBJEXT) \
@@ -269,13 +275,15 @@ am_ModemManager_OBJECTS = ModemManager-main.$(OBJEXT) \
ModemManager-mm-iface-modem-messaging.$(OBJEXT) \
ModemManager-mm-iface-modem-time.$(OBJEXT) \
ModemManager-mm-iface-modem-firmware.$(OBJEXT) \
+ ModemManager-mm-iface-modem-signal.$(OBJEXT) \
+ ModemManager-mm-iface-modem-oma.$(OBJEXT) \
ModemManager-mm-broadband-modem.$(OBJEXT) \
ModemManager-mm-serial-parsers.$(OBJEXT) \
ModemManager-mm-port-probe.$(OBJEXT) \
ModemManager-mm-port-probe-at.$(OBJEXT) \
ModemManager-mm-plugin.$(OBJEXT) $(am__objects_5) \
$(am__objects_6) $(am__objects_7)
-nodist_ModemManager_OBJECTS = ModemManager-mm-marshal.$(OBJEXT) \
+nodist_ModemManager_OBJECTS = \
ModemManager-mm-daemon-enums-types.$(OBJEXT)
ModemManager_OBJECTS = $(am_ModemManager_OBJECTS) \
$(nodist_ModemManager_OBJECTS)
@@ -481,6 +489,14 @@ INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@
+INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@
+INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@
+INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@
+INTROSPECTION_LIBS = @INTROSPECTION_LIBS@
+INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
+INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
+INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBICONV = @LIBICONV@
@@ -547,6 +563,9 @@ STRIP = @STRIP@
SYSTEMD_UNIT_DIR = @SYSTEMD_UNIT_DIR@
UDEV_BASE_DIR = @UDEV_BASE_DIR@
USE_NLS = @USE_NLS@
+VAPIGEN = @VAPIGEN@
+VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@
+VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@
VERSION = @VERSION@
XGETTEXT = @XGETTEXT@
XGETTEXT_015 = @XGETTEXT_015@
@@ -626,8 +645,9 @@ libmodem_helpers_la_LIBADD = \
libmodem_helpers_la_SOURCES = mm-error-helpers.c mm-error-helpers.h \
mm-modem-helpers.c mm-modem-helpers.h mm-charsets.c \
- mm-charsets.h mm-sms-part.h mm-sms-part.c $(am__append_1) \
- $(am__append_3)
+ mm-charsets.h mm-sms-part.h mm-sms-part.c mm-sms-part-3gpp.h \
+ mm-sms-part-3gpp.c mm-sms-part-cdma.h mm-sms-part-cdma.c \
+ $(am__append_1) $(am__append_3)
# libserial specific enum types
SERIAL_ENUMS = \
@@ -667,8 +687,6 @@ ModemManager_LDADD = $(MM_LIBS) $(GUDEV_LIBS) \
$(top_builddir)/libqcdm/src/libqcdm.la $(am__append_10) \
$(am__append_14) $(am__append_17)
nodist_ModemManager_SOURCES = \
- mm-marshal.h \
- mm-marshal.c \
mm-daemon-enums-types.h \
mm-daemon-enums-types.c
@@ -680,27 +698,26 @@ ModemManager_SOURCES = main.c mm-context.h mm-context.c mm-log.c \
mm-bearer.h mm-bearer.c mm-broadband-bearer.h \
mm-broadband-bearer.c mm-bearer-list.h mm-bearer-list.c \
mm-base-modem-at.h mm-base-modem-at.c mm-base-modem.h \
- mm-base-modem.c mm-sms-part.h mm-sms-part.c mm-sms.h mm-sms.c \
- mm-sms-list.h mm-sms-list.c mm-iface-modem.h mm-iface-modem.c \
- mm-iface-modem-3gpp.h mm-iface-modem-3gpp.c \
- mm-iface-modem-3gpp-ussd.h mm-iface-modem-3gpp-ussd.c \
- mm-iface-modem-cdma.h mm-iface-modem-cdma.c \
- mm-iface-modem-simple.h mm-iface-modem-simple.c \
- mm-iface-modem-location.h mm-iface-modem-location.c \
- mm-iface-modem-messaging.h mm-iface-modem-messaging.c \
- mm-iface-modem-time.h mm-iface-modem-time.c \
- mm-iface-modem-firmware.h mm-iface-modem-firmware.c \
- mm-broadband-modem.h mm-broadband-modem.c mm-serial-parsers.c \
- mm-serial-parsers.h mm-port-probe.h mm-port-probe.c \
- mm-port-probe-at.h mm-port-probe-at.c mm-plugin.c mm-plugin.h \
- $(am__append_9) $(am__append_12) $(am__append_15)
+ mm-base-modem.c mm-sms.h mm-sms.c mm-sms-list.h mm-sms-list.c \
+ mm-iface-modem.h mm-iface-modem.c mm-iface-modem-3gpp.h \
+ mm-iface-modem-3gpp.c mm-iface-modem-3gpp-ussd.h \
+ mm-iface-modem-3gpp-ussd.c mm-iface-modem-cdma.h \
+ mm-iface-modem-cdma.c mm-iface-modem-simple.h \
+ mm-iface-modem-simple.c mm-iface-modem-location.h \
+ mm-iface-modem-location.c mm-iface-modem-messaging.h \
+ mm-iface-modem-messaging.c mm-iface-modem-time.h \
+ mm-iface-modem-time.c mm-iface-modem-firmware.h \
+ mm-iface-modem-firmware.c mm-iface-modem-signal.h \
+ mm-iface-modem-signal.c mm-iface-modem-oma.h \
+ mm-iface-modem-oma.c mm-broadband-modem.h mm-broadband-modem.c \
+ mm-serial-parsers.c mm-serial-parsers.h mm-port-probe.h \
+ mm-port-probe.c mm-port-probe-at.h mm-port-probe-at.c \
+ mm-plugin.c mm-plugin.h $(am__append_9) $(am__append_12) \
+ $(am__append_15)
EXTRA_DIST = \
- $(udevrules_DATA) \
- mm-marshal.list
+ $(udevrules_DATA)
CLEANFILES = \
- mm-marshal.h \
- mm-marshal.c \
mm-daemon-enums-types.h \
mm-daemon-enums-types.c \
mm-serial-enums-types.h \
@@ -840,12 +857,13 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-iface-modem-firmware.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-iface-modem-location.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-iface-modem-messaging.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-iface-modem-oma.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-iface-modem-signal.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-iface-modem-simple.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-iface-modem-time.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-iface-modem.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-log.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-manager.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-marshal.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-plugin-manager.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-plugin.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-port-probe-at.Po@am__quote@
@@ -857,7 +875,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-sim.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-sms-list.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-sms-mbim.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-sms-part.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-sms-qmi.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ModemManager-mm-sms.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_helpers_la-mm-charsets.Plo@am__quote@
@@ -865,6 +882,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_helpers_la-mm-modem-helpers-mbim.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_helpers_la-mm-modem-helpers-qmi.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_helpers_la-mm-modem-helpers.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_helpers_la-mm-sms-part-3gpp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_helpers_la-mm-sms-part-cdma.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmodem_helpers_la-mm-sms-part.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libserial_la-mm-at-serial-port.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libserial_la-mm-gps-serial-port.Plo@am__quote@
@@ -927,6 +946,20 @@ libmodem_helpers_la-mm-sms-part.lo: mm-sms-part.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_helpers_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmodem_helpers_la-mm-sms-part.lo `test -f 'mm-sms-part.c' || echo '$(srcdir)/'`mm-sms-part.c
+libmodem_helpers_la-mm-sms-part-3gpp.lo: mm-sms-part-3gpp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_helpers_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmodem_helpers_la-mm-sms-part-3gpp.lo -MD -MP -MF $(DEPDIR)/libmodem_helpers_la-mm-sms-part-3gpp.Tpo -c -o libmodem_helpers_la-mm-sms-part-3gpp.lo `test -f 'mm-sms-part-3gpp.c' || echo '$(srcdir)/'`mm-sms-part-3gpp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmodem_helpers_la-mm-sms-part-3gpp.Tpo $(DEPDIR)/libmodem_helpers_la-mm-sms-part-3gpp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mm-sms-part-3gpp.c' object='libmodem_helpers_la-mm-sms-part-3gpp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_helpers_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmodem_helpers_la-mm-sms-part-3gpp.lo `test -f 'mm-sms-part-3gpp.c' || echo '$(srcdir)/'`mm-sms-part-3gpp.c
+
+libmodem_helpers_la-mm-sms-part-cdma.lo: mm-sms-part-cdma.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_helpers_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmodem_helpers_la-mm-sms-part-cdma.lo -MD -MP -MF $(DEPDIR)/libmodem_helpers_la-mm-sms-part-cdma.Tpo -c -o libmodem_helpers_la-mm-sms-part-cdma.lo `test -f 'mm-sms-part-cdma.c' || echo '$(srcdir)/'`mm-sms-part-cdma.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmodem_helpers_la-mm-sms-part-cdma.Tpo $(DEPDIR)/libmodem_helpers_la-mm-sms-part-cdma.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mm-sms-part-cdma.c' object='libmodem_helpers_la-mm-sms-part-cdma.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_helpers_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmodem_helpers_la-mm-sms-part-cdma.lo `test -f 'mm-sms-part-cdma.c' || echo '$(srcdir)/'`mm-sms-part-cdma.c
+
libmodem_helpers_la-mm-modem-helpers-qmi.lo: mm-modem-helpers-qmi.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmodem_helpers_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libmodem_helpers_la-mm-modem-helpers-qmi.lo -MD -MP -MF $(DEPDIR)/libmodem_helpers_la-mm-modem-helpers-qmi.Tpo -c -o libmodem_helpers_la-mm-modem-helpers-qmi.lo `test -f 'mm-modem-helpers-qmi.c' || echo '$(srcdir)/'`mm-modem-helpers-qmi.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmodem_helpers_la-mm-modem-helpers-qmi.Tpo $(DEPDIR)/libmodem_helpers_la-mm-modem-helpers-qmi.Plo
@@ -1207,20 +1240,6 @@ ModemManager-mm-base-modem.obj: mm-base-modem.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ModemManager-mm-base-modem.obj `if test -f 'mm-base-modem.c'; then $(CYGPATH_W) 'mm-base-modem.c'; else $(CYGPATH_W) '$(srcdir)/mm-base-modem.c'; fi`
-ModemManager-mm-sms-part.o: mm-sms-part.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ModemManager-mm-sms-part.o -MD -MP -MF $(DEPDIR)/ModemManager-mm-sms-part.Tpo -c -o ModemManager-mm-sms-part.o `test -f 'mm-sms-part.c' || echo '$(srcdir)/'`mm-sms-part.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ModemManager-mm-sms-part.Tpo $(DEPDIR)/ModemManager-mm-sms-part.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mm-sms-part.c' object='ModemManager-mm-sms-part.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ModemManager-mm-sms-part.o `test -f 'mm-sms-part.c' || echo '$(srcdir)/'`mm-sms-part.c
-
-ModemManager-mm-sms-part.obj: mm-sms-part.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ModemManager-mm-sms-part.obj -MD -MP -MF $(DEPDIR)/ModemManager-mm-sms-part.Tpo -c -o ModemManager-mm-sms-part.obj `if test -f 'mm-sms-part.c'; then $(CYGPATH_W) 'mm-sms-part.c'; else $(CYGPATH_W) '$(srcdir)/mm-sms-part.c'; fi`
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ModemManager-mm-sms-part.Tpo $(DEPDIR)/ModemManager-mm-sms-part.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mm-sms-part.c' object='ModemManager-mm-sms-part.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ModemManager-mm-sms-part.obj `if test -f 'mm-sms-part.c'; then $(CYGPATH_W) 'mm-sms-part.c'; else $(CYGPATH_W) '$(srcdir)/mm-sms-part.c'; fi`
-
ModemManager-mm-sms.o: mm-sms.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ModemManager-mm-sms.o -MD -MP -MF $(DEPDIR)/ModemManager-mm-sms.Tpo -c -o ModemManager-mm-sms.o `test -f 'mm-sms.c' || echo '$(srcdir)/'`mm-sms.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ModemManager-mm-sms.Tpo $(DEPDIR)/ModemManager-mm-sms.Po
@@ -1375,6 +1394,34 @@ ModemManager-mm-iface-modem-firmware.obj: mm-iface-modem-firmware.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ModemManager-mm-iface-modem-firmware.obj `if test -f 'mm-iface-modem-firmware.c'; then $(CYGPATH_W) 'mm-iface-modem-firmware.c'; else $(CYGPATH_W) '$(srcdir)/mm-iface-modem-firmware.c'; fi`
+ModemManager-mm-iface-modem-signal.o: mm-iface-modem-signal.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ModemManager-mm-iface-modem-signal.o -MD -MP -MF $(DEPDIR)/ModemManager-mm-iface-modem-signal.Tpo -c -o ModemManager-mm-iface-modem-signal.o `test -f 'mm-iface-modem-signal.c' || echo '$(srcdir)/'`mm-iface-modem-signal.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ModemManager-mm-iface-modem-signal.Tpo $(DEPDIR)/ModemManager-mm-iface-modem-signal.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mm-iface-modem-signal.c' object='ModemManager-mm-iface-modem-signal.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ModemManager-mm-iface-modem-signal.o `test -f 'mm-iface-modem-signal.c' || echo '$(srcdir)/'`mm-iface-modem-signal.c
+
+ModemManager-mm-iface-modem-signal.obj: mm-iface-modem-signal.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ModemManager-mm-iface-modem-signal.obj -MD -MP -MF $(DEPDIR)/ModemManager-mm-iface-modem-signal.Tpo -c -o ModemManager-mm-iface-modem-signal.obj `if test -f 'mm-iface-modem-signal.c'; then $(CYGPATH_W) 'mm-iface-modem-signal.c'; else $(CYGPATH_W) '$(srcdir)/mm-iface-modem-signal.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ModemManager-mm-iface-modem-signal.Tpo $(DEPDIR)/ModemManager-mm-iface-modem-signal.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mm-iface-modem-signal.c' object='ModemManager-mm-iface-modem-signal.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ModemManager-mm-iface-modem-signal.obj `if test -f 'mm-iface-modem-signal.c'; then $(CYGPATH_W) 'mm-iface-modem-signal.c'; else $(CYGPATH_W) '$(srcdir)/mm-iface-modem-signal.c'; fi`
+
+ModemManager-mm-iface-modem-oma.o: mm-iface-modem-oma.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ModemManager-mm-iface-modem-oma.o -MD -MP -MF $(DEPDIR)/ModemManager-mm-iface-modem-oma.Tpo -c -o ModemManager-mm-iface-modem-oma.o `test -f 'mm-iface-modem-oma.c' || echo '$(srcdir)/'`mm-iface-modem-oma.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ModemManager-mm-iface-modem-oma.Tpo $(DEPDIR)/ModemManager-mm-iface-modem-oma.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mm-iface-modem-oma.c' object='ModemManager-mm-iface-modem-oma.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ModemManager-mm-iface-modem-oma.o `test -f 'mm-iface-modem-oma.c' || echo '$(srcdir)/'`mm-iface-modem-oma.c
+
+ModemManager-mm-iface-modem-oma.obj: mm-iface-modem-oma.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ModemManager-mm-iface-modem-oma.obj -MD -MP -MF $(DEPDIR)/ModemManager-mm-iface-modem-oma.Tpo -c -o ModemManager-mm-iface-modem-oma.obj `if test -f 'mm-iface-modem-oma.c'; then $(CYGPATH_W) 'mm-iface-modem-oma.c'; else $(CYGPATH_W) '$(srcdir)/mm-iface-modem-oma.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ModemManager-mm-iface-modem-oma.Tpo $(DEPDIR)/ModemManager-mm-iface-modem-oma.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mm-iface-modem-oma.c' object='ModemManager-mm-iface-modem-oma.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ModemManager-mm-iface-modem-oma.obj `if test -f 'mm-iface-modem-oma.c'; then $(CYGPATH_W) 'mm-iface-modem-oma.c'; else $(CYGPATH_W) '$(srcdir)/mm-iface-modem-oma.c'; fi`
+
ModemManager-mm-broadband-modem.o: mm-broadband-modem.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ModemManager-mm-broadband-modem.o -MD -MP -MF $(DEPDIR)/ModemManager-mm-broadband-modem.Tpo -c -o ModemManager-mm-broadband-modem.o `test -f 'mm-broadband-modem.c' || echo '$(srcdir)/'`mm-broadband-modem.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ModemManager-mm-broadband-modem.Tpo $(DEPDIR)/ModemManager-mm-broadband-modem.Po
@@ -1571,20 +1618,6 @@ ModemManager-mm-broadband-modem-mbim.obj: mm-broadband-modem-mbim.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ModemManager-mm-broadband-modem-mbim.obj `if test -f 'mm-broadband-modem-mbim.c'; then $(CYGPATH_W) 'mm-broadband-modem-mbim.c'; else $(CYGPATH_W) '$(srcdir)/mm-broadband-modem-mbim.c'; fi`
-ModemManager-mm-marshal.o: mm-marshal.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ModemManager-mm-marshal.o -MD -MP -MF $(DEPDIR)/ModemManager-mm-marshal.Tpo -c -o ModemManager-mm-marshal.o `test -f 'mm-marshal.c' || echo '$(srcdir)/'`mm-marshal.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ModemManager-mm-marshal.Tpo $(DEPDIR)/ModemManager-mm-marshal.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mm-marshal.c' object='ModemManager-mm-marshal.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ModemManager-mm-marshal.o `test -f 'mm-marshal.c' || echo '$(srcdir)/'`mm-marshal.c
-
-ModemManager-mm-marshal.obj: mm-marshal.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ModemManager-mm-marshal.obj -MD -MP -MF $(DEPDIR)/ModemManager-mm-marshal.Tpo -c -o ModemManager-mm-marshal.obj `if test -f 'mm-marshal.c'; then $(CYGPATH_W) 'mm-marshal.c'; else $(CYGPATH_W) '$(srcdir)/mm-marshal.c'; fi`
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ModemManager-mm-marshal.Tpo $(DEPDIR)/ModemManager-mm-marshal.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mm-marshal.c' object='ModemManager-mm-marshal.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ModemManager-mm-marshal.obj `if test -f 'mm-marshal.c'; then $(CYGPATH_W) 'mm-marshal.c'; else $(CYGPATH_W) '$(srcdir)/mm-marshal.c'; fi`
-
ModemManager-mm-daemon-enums-types.o: mm-daemon-enums-types.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ModemManager_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ModemManager-mm-daemon-enums-types.o -MD -MP -MF $(DEPDIR)/ModemManager-mm-daemon-enums-types.Tpo -c -o ModemManager-mm-daemon-enums-types.o `test -f 'mm-daemon-enums-types.c' || echo '$(srcdir)/'`mm-daemon-enums-types.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ModemManager-mm-daemon-enums-types.Tpo $(DEPDIR)/ModemManager-mm-daemon-enums-types.Po
@@ -1935,19 +1968,8 @@ mm-daemon-enums-types.c: Makefile.am $(top_srcdir)/build-aux/mm-enums-template.c
--template $(top_srcdir)/build-aux/mm-enums-template.c \
$(DAEMON_ENUMS) > $@
-# Marshallers
-
-mm-marshal.h: mm-marshal.list
- $(AM_V_GEN) $(GLIB_GENMARSHAL) $< --prefix=mm_marshal --header > $@
-
-mm-marshal.c: mm-marshal.list mm-marshal.h
- $(AM_V_GEN) echo "#include \"mm-marshal.h\"" > $@ && \
- $(GLIB_GENMARSHAL) $< --prefix=mm_marshal --body >> $@
-
# Additional dependency rules
mm-bearer.c: mm-daemon-enums-types.h
-mm-sms-list.c: mm-marshal.h
-mm-sim.c: mm-marshal.h
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/src/mm-at-serial-port.c b/src/mm-at-serial-port.c
index 346221a..1c8e355 100644
--- a/src/mm-at-serial-port.c
+++ b/src/mm-at-serial-port.c
@@ -176,6 +176,7 @@ handle_response (MMSerialPort *port,
typedef struct {
GRegex *regex;
MMAtSerialUnsolicitedMsgFn callback;
+ gboolean enable;
gpointer user_data;
GDestroyNotify notify;
} MMAtUnsolicitedMsgHandler;
@@ -219,10 +220,34 @@ mm_at_serial_port_add_unsolicited_msg_handler (MMAtSerialPort *self,
}
handler->callback = callback;
+ handler->enable = TRUE;
handler->user_data = user_data;
handler->notify = notify;
}
+void
+mm_at_serial_port_enable_unsolicited_msg_handler (MMAtSerialPort *self,
+ GRegex *regex,
+ gboolean enable)
+{
+ GSList *existing;
+ MMAtUnsolicitedMsgHandler *handler;
+ MMAtSerialPortPrivate *priv;
+
+ g_return_if_fail (MM_IS_AT_SERIAL_PORT (self));
+ g_return_if_fail (regex != NULL);
+
+ priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
+
+ existing = g_slist_find_custom (priv->unsolicited_msg_handlers,
+ regex,
+ (GCompareFunc)unsolicited_msg_handler_cmp);
+ if (existing) {
+ handler = existing->data;
+ handler->enable = enable;
+ }
+}
+
static gboolean
remove_eval_cb (const GMatchInfo *match_info,
GString *result,
@@ -254,6 +279,9 @@ parse_unsolicited (MMSerialPort *port, GByteArray *response)
GMatchInfo *match_info;
gboolean matches;
+ if (!handler->enable)
+ continue;
+
matches = g_regex_match_full (handler->regex,
(const char *) response->data,
response->len,
diff --git a/src/mm-at-serial-port.h b/src/mm-at-serial-port.h
index cf960a0..cdd3b9c 100644
--- a/src/mm-at-serial-port.h
+++ b/src/mm-at-serial-port.h
@@ -89,6 +89,10 @@ void mm_at_serial_port_add_unsolicited_msg_handler (MMAtSerialPort *self,
gpointer user_data,
GDestroyNotify notify);
+void mm_at_serial_port_enable_unsolicited_msg_handler (MMAtSerialPort *self,
+ GRegex *regex,
+ gboolean enable);
+
void mm_at_serial_port_set_response_parser (MMAtSerialPort *self,
MMAtSerialResponseParserFn fn,
gpointer user_data,
diff --git a/src/mm-base-modem.c b/src/mm-base-modem.c
index b348aba..f6f448f 100644
--- a/src/mm-base-modem.c
+++ b/src/mm-base-modem.c
@@ -295,7 +295,8 @@ mm_base_modem_release_port (MMBaseModem *self,
g_return_if_fail (subsys != NULL);
if (!g_str_equal (subsys, "tty") &&
- !g_str_equal (subsys, "net"))
+ !g_str_equal (subsys, "net") &&
+ !(g_str_has_prefix (subsys, "usb") && g_str_has_prefix (name, "cdc-wdm")))
return;
key = get_hash_key (subsys, name);
@@ -674,7 +675,7 @@ mm_base_modem_peek_port_qmi_for_data (MMBaseModem *self,
qmi_device_parent = g_udev_device_get_parent (qmi_device);
g_object_unref (qmi_device);
- if (!data_device_parent) {
+ if (!qmi_device_parent) {
mm_warn ("Couldn't get udev device parent for QMI port '%s'",
mm_port_get_device (MM_PORT (l->data)));
continue;
@@ -825,7 +826,7 @@ mm_base_modem_peek_port_mbim_for_data (MMBaseModem *self,
mbim_device_parent = g_udev_device_get_parent (mbim_device);
g_object_unref (mbim_device);
- if (!data_device_parent) {
+ if (!mbim_device_parent) {
mm_warn ("Couldn't get udev device parent for MBIM port '%s'",
mm_port_get_device (MM_PORT (l->data)));
continue;
diff --git a/src/mm-bearer-list.c b/src/mm-bearer-list.c
index 43ccf8f..278ceba 100644
--- a/src/mm-bearer-list.c
+++ b/src/mm-bearer-list.c
@@ -33,6 +33,7 @@ G_DEFINE_TYPE (MMBearerList, mm_bearer_list, G_TYPE_OBJECT);
enum {
PROP_0,
+ PROP_NUM_BEARERS,
PROP_MAX_BEARERS,
PROP_MAX_ACTIVE_BEARERS,
PROP_LAST
@@ -91,8 +92,9 @@ mm_bearer_list_add_bearer (MMBearerList *self,
}
/* Keep our own reference */
- self->priv->bearers = g_list_prepend (self->priv->bearers,
- g_object_ref (bearer));
+ self->priv->bearers = g_list_prepend (self->priv->bearers, g_object_ref (bearer));
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NUM_BEARERS]);
+
return TRUE;
}
@@ -115,8 +117,8 @@ mm_bearer_list_delete_bearer (MMBearerList *self,
for (l = self->priv->bearers; l; l = g_list_next (l)) {
if (g_str_equal (path, mm_bearer_get_path (MM_BEARER (l->data)))) {
g_object_unref (l->data);
- self->priv->bearers =
- g_list_delete_link (self->priv->bearers, l);
+ self->priv->bearers = g_list_delete_link (self->priv->bearers, l);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NUM_BEARERS]);
return TRUE;
}
}
@@ -137,6 +139,7 @@ mm_bearer_list_delete_all_bearers (MMBearerList *self)
g_list_free_full (self->priv->bearers, (GDestroyNotify) g_object_unref);
self->priv->bearers = NULL;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_NUM_BEARERS]);
}
GStrv
@@ -288,6 +291,9 @@ set_property (GObject *object,
MMBearerList *self = MM_BEARER_LIST (object);
switch (prop_id) {
+ case PROP_NUM_BEARERS:
+ g_assert_not_reached ();
+ break;
case PROP_MAX_BEARERS:
self->priv->max_bearers = g_value_get_uint (value);
break;
@@ -309,6 +315,9 @@ get_property (GObject *object,
MMBearerList *self = MM_BEARER_LIST (object);
switch (prop_id) {
+ case PROP_NUM_BEARERS:
+ g_value_set_uint (value, g_list_length (self->priv->bearers));
+ break;
case PROP_MAX_BEARERS:
g_value_set_uint (value, self->priv->max_bearers);
break;
@@ -352,6 +361,16 @@ mm_bearer_list_class_init (MMBearerListClass *klass)
object_class->set_property = set_property;
object_class->dispose = dispose;
+ properties[PROP_NUM_BEARERS] =
+ g_param_spec_uint (MM_BEARER_LIST_NUM_BEARERS,
+ "Number of bearers",
+ "Current number of bearers in the list",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_NUM_BEARERS, properties[PROP_NUM_BEARERS]);
+
properties[PROP_MAX_BEARERS] =
g_param_spec_uint (MM_BEARER_LIST_MAX_BEARERS,
"Max bearers",
diff --git a/src/mm-bearer-list.h b/src/mm-bearer-list.h
index 7cd0394..292e3d5 100644
--- a/src/mm-bearer-list.h
+++ b/src/mm-bearer-list.h
@@ -30,6 +30,7 @@
#define MM_IS_BEARER_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BEARER_LIST))
#define MM_BEARER_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BEARER_LIST, MMBearerListClass))
+#define MM_BEARER_LIST_NUM_BEARERS "num-bearers"
#define MM_BEARER_LIST_MAX_BEARERS "max-bearers"
#define MM_BEARER_LIST_MAX_ACTIVE_BEARERS "max-active-bearers"
diff --git a/src/mm-bearer-mbim.c b/src/mm-bearer-mbim.c
index 2cfd1cf..ff5c6a0 100644
--- a/src/mm-bearer-mbim.c
+++ b/src/mm-bearer-mbim.c
@@ -139,6 +139,7 @@ connect_context_complete_and_free (ConnectContext *ctx)
mm_bearer_connect_result_unref (ctx->connect_result);
g_object_unref (ctx->data);
g_object_unref (ctx->cancellable);
+ g_object_unref (ctx->properties);
g_object_unref (ctx->device);
g_object_unref (ctx->self);
g_slice_free (ConnectContext, ctx);
@@ -398,12 +399,19 @@ ip_configuration_query_ready (MbimDevice *device,
ipv4_config,
ipv6_config);
+ if (ipv4_config)
+ g_object_unref (ipv4_config);
+ if (ipv6_config)
+ g_object_unref (ipv6_config);
mbim_ipv4_element_array_free (ipv4address);
mbim_ipv6_element_array_free (ipv6address);
g_free (ipv4dnsserver);
g_free (ipv6dnsserver);
}
+ if (response)
+ mbim_message_unref (response);
+
if (error) {
g_simple_async_result_take_error (ctx->result, error);
connect_context_complete_and_free (ctx);
@@ -429,27 +437,42 @@ connect_set_ready (MbimDevice *device,
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
- mbim_message_connect_response_parse (
- response,
- &session_id,
- &activation_state,
- NULL, /* voice_call_state */
- &ip_type,
- NULL, /* context_type */
- &nw_error,
- &error)) {
- if (nw_error)
- error = mm_mobile_equipment_error_from_mbim_nw_error (nw_error);
- else {
- ctx->ip_type = ip_type;
- mm_dbg ("Session ID '%u': %s (IP type: %s)",
- session_id,
- mbim_activation_state_get_string (activation_state),
- mbim_context_ip_type_get_string (ip_type));
+ (mbim_message_command_done_get_result (response, &error) ||
+ error->code == MBIM_STATUS_ERROR_FAILURE)) {
+ GError *inner_error = NULL;
+
+ if (mbim_message_connect_response_parse (
+ response,
+ &session_id,
+ &activation_state,
+ NULL, /* voice_call_state */
+ &ip_type,
+ NULL, /* context_type */
+ &nw_error,
+ &inner_error)) {
+ if (nw_error) {
+ if (error)
+ g_error_free (error);
+ error = mm_mobile_equipment_error_from_mbim_nw_error (nw_error);
+ } else {
+ ctx->ip_type = ip_type;
+ mm_dbg ("Session ID '%u': %s (IP type: %s)",
+ session_id,
+ mbim_activation_state_get_string (activation_state),
+ mbim_context_ip_type_get_string (ip_type));
+ }
+ } else {
+ /* Prefer the error from the result to the parsing error */
+ if (!error)
+ error = inner_error;
+ else
+ g_error_free (inner_error);
}
}
+ if (response)
+ mbim_message_unref (response);
+
if (error) {
g_simple_async_result_take_error (ctx->result, error);
connect_context_complete_and_free (ctx);
@@ -503,6 +526,9 @@ provisioned_contexts_query_ready (MbimDevice *device,
g_error_free (error);
}
+ if (response)
+ mbim_message_unref (response);
+
/* Keep on */
ctx->step++;
connect_context_step (ctx);
@@ -523,30 +549,45 @@ packet_service_set_ready (MbimDevice *device,
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
- mbim_message_packet_service_response_parse (
- response,
- &nw_error,
- &packet_service_state,
- &highest_available_data_class,
- &uplink_speed,
- &downlink_speed,
- &error)) {
- if (nw_error)
- error = mm_mobile_equipment_error_from_mbim_nw_error (nw_error);
- else {
- gchar *str;
-
- str = mbim_data_class_build_string_from_mask (highest_available_data_class);
- mm_dbg ("Packet service update:");
- mm_dbg (" state: '%s'", mbim_packet_service_state_get_string (packet_service_state));
- mm_dbg (" data class: '%s'", str);
- mm_dbg (" uplink: '%" G_GUINT64_FORMAT "' bps", uplink_speed);
- mm_dbg (" downlink: '%" G_GUINT64_FORMAT "' bps", downlink_speed);
- g_free (str);
+ (mbim_message_command_done_get_result (response, &error) ||
+ error->code == MBIM_STATUS_ERROR_FAILURE)) {
+ GError *inner_error = NULL;
+
+ if (mbim_message_packet_service_response_parse (
+ response,
+ &nw_error,
+ &packet_service_state,
+ &highest_available_data_class,
+ &uplink_speed,
+ &downlink_speed,
+ &error)) {
+ if (nw_error) {
+ if (error)
+ g_error_free (error);
+ error = mm_mobile_equipment_error_from_mbim_nw_error (nw_error);
+ } else {
+ gchar *str;
+
+ str = mbim_data_class_build_string_from_mask (highest_available_data_class);
+ mm_dbg ("Packet service update:");
+ mm_dbg (" state: '%s'", mbim_packet_service_state_get_string (packet_service_state));
+ mm_dbg (" data class: '%s'", str);
+ mm_dbg (" uplink: '%" G_GUINT64_FORMAT "' bps", uplink_speed);
+ mm_dbg (" downlink: '%" G_GUINT64_FORMAT "' bps", downlink_speed);
+ g_free (str);
+ }
+ } else {
+ /* Prefer the error from the result to the parsing error */
+ if (!error)
+ error = inner_error;
+ else
+ g_error_free (inner_error);
}
}
+ if (response)
+ mbim_message_unref (response);
+
if (error) {
/* Don't make NoDeviceSupport errors fatal; just try to keep on the
* connection sequence even with this error. */
@@ -902,24 +943,39 @@ disconnect_set_ready (MbimDevice *device,
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
- mbim_message_connect_response_parse (
- response,
- &session_id,
- &activation_state,
- NULL, /* voice_call_state */
- NULL, /* ip_type */
- NULL, /* context_type */
- &nw_error,
- &error)) {
- if (nw_error)
- error = mm_mobile_equipment_error_from_mbim_nw_error (nw_error);
- else
- mm_dbg ("Session ID '%u': %s",
- session_id,
- mbim_activation_state_get_string (activation_state));
+ (mbim_message_command_done_get_result (response, &error) ||
+ error->code == MBIM_STATUS_ERROR_FAILURE)) {
+ GError *inner_error = NULL;
+
+ if (mbim_message_connect_response_parse (
+ response,
+ &session_id,
+ &activation_state,
+ NULL, /* voice_call_state */
+ NULL, /* ip_type */
+ NULL, /* context_type */
+ &nw_error,
+ &error)) {
+ if (nw_error) {
+ if (error)
+ g_error_free (error);
+ error = mm_mobile_equipment_error_from_mbim_nw_error (nw_error);
+ } else
+ mm_dbg ("Session ID '%u': %s",
+ session_id,
+ mbim_activation_state_get_string (activation_state));
+ } else {
+ /* Prefer the error from the result to the parsing error */
+ if (!error)
+ error = inner_error;
+ else
+ g_error_free (inner_error);
+ }
}
+ if (response)
+ mbim_message_unref (response);
+
if (error) {
g_simple_async_result_take_error (ctx->result, error);
disconnect_context_complete_and_free (ctx);
diff --git a/src/mm-bearer-qmi.c b/src/mm-bearer-qmi.c
index 00d17c5..a8341db 100644
--- a/src/mm-bearer-qmi.c
+++ b/src/mm-bearer-qmi.c
@@ -344,6 +344,9 @@ get_current_settings_ready (QmiClientWds *client,
success = qmi_message_wds_get_current_settings_output_get_secondary_ipv4_dns_address (output, &addr, &error);
print_address4 (success, " DNS #2", addr, error);
g_clear_error (&error);
+ } else {
+ /* no IPv4 configuration */
+ g_clear_error (&error);
}
/* If the message has an IPv6 address, print IPv6 settings */
@@ -368,6 +371,9 @@ get_current_settings_ready (QmiClientWds *client,
success = qmi_message_wds_get_current_settings_output_get_ipv6_secondary_dns_address (output, &array, &error);
print_address6 (success, " DNS #2", array, 0, error);
g_clear_error (&error);
+ } else {
+ /* no IPv6 configuration */
+ g_clear_error (&error);
}
/* Domain names */
@@ -1186,13 +1192,15 @@ disconnect (MMBearer *_self,
/*****************************************************************************/
static void
-report_disconnection (MMBearer *self)
+report_connection_status (MMBearer *self,
+ MMBearerConnectionStatus status)
{
- /* Cleanup all connection related data */
- reset_bearer_connection (MM_BEARER_QMI (self), TRUE, TRUE);
+ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED)
+ /* Cleanup all connection related data */
+ reset_bearer_connection (MM_BEARER_QMI (self), TRUE, TRUE);
- /* Chain up parent's report_disconection() */
- MM_BEARER_CLASS (mm_bearer_qmi_parent_class)->report_disconnection (self);
+ /* Chain up parent's report_connection_status() */
+ MM_BEARER_CLASS (mm_bearer_qmi_parent_class)->report_connection_status (self, status);
}
/*****************************************************************************/
@@ -1253,5 +1261,5 @@ mm_bearer_qmi_class_init (MMBearerQmiClass *klass)
bearer_class->connect_finish = connect_finish;
bearer_class->disconnect = disconnect;
bearer_class->disconnect_finish = disconnect_finish;
- bearer_class->report_disconnection = report_disconnection;
+ bearer_class->report_connection_status = report_connection_status;
}
diff --git a/src/mm-bearer.c b/src/mm-bearer.c
index 8c9a93c..6592619 100644
--- a/src/mm-bearer.c
+++ b/src/mm-bearer.c
@@ -441,7 +441,12 @@ disconnect_after_cancel_ready (MMBearer *self,
else
mm_dbg ("Disconnected bearer '%s'", self->priv->path);
- bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
+ /* Report disconnection to the bearer object using class method
+ * mm_bearer_report_connection_status. This gives subclass implementations a
+ * chance to correctly update their own connection state, in case this base
+ * class ignores a failed disconnection attempt.
+ */
+ mm_bearer_report_connection_status (self, MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
}
static void
@@ -945,7 +950,12 @@ disconnect_force_ready (MMBearer *self,
else
mm_dbg ("Disconnected bearer '%s'", self->priv->path);
- bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
+ /* Report disconnection to the bearer object using class method
+ * mm_bearer_report_connection_status. This gives subclass implementations a
+ * chance to correctly update their own connection state, in case this base
+ * class ignores a failed disconnection attempt.
+ */
+ mm_bearer_report_connection_status (self, MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
}
void
@@ -974,17 +984,25 @@ mm_bearer_disconnect_force (MMBearer *self)
/*****************************************************************************/
static void
-report_disconnection (MMBearer *self)
+report_connection_status (MMBearer *self,
+ MMBearerConnectionStatus status)
{
+ /* The only status expected at this point is DISCONNECTED.
+ * No other status should have been given to the generic implementation
+ * of report_connection_status (it would be an error).
+ */
+ g_assert (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+
/* In the generic bearer implementation we just need to reset the
* interface status */
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
}
void
-mm_bearer_report_disconnection (MMBearer *self)
+mm_bearer_report_connection_status (MMBearer *self,
+ MMBearerConnectionStatus status)
{
- return MM_BEARER_GET_CLASS (self)->report_disconnection (self);
+ return MM_BEARER_GET_CLASS (self)->report_connection_status (self, status);
}
static void
@@ -1160,7 +1178,7 @@ mm_bearer_class_init (MMBearerClass *klass)
object_class->finalize = finalize;
object_class->dispose = dispose;
- klass->report_disconnection = report_disconnection;
+ klass->report_connection_status = report_connection_status;
properties[PROP_CONNECTION] =
g_param_spec_object (MM_BEARER_CONNECTION,
diff --git a/src/mm-bearer.h b/src/mm-bearer.h
index cc71bfd..c1bcaee 100644
--- a/src/mm-bearer.h
+++ b/src/mm-bearer.h
@@ -67,6 +67,14 @@ typedef enum { /*< underscore_name=mm_bearer_status >*/
MM_BEARER_STATUS_CONNECTED,
} MMBearerStatus;
+typedef enum { /*< underscore_name=mm_bearer_connection_status >*/
+ MM_BEARER_CONNECTION_STATUS_UNKNOWN,
+ MM_BEARER_CONNECTION_STATUS_DISCONNECTED,
+ MM_BEARER_CONNECTION_STATUS_DISCONNECTING,
+ MM_BEARER_CONNECTION_STATUS_CONNECTED,
+ MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED,
+} MMBearerConnectionStatus;
+
struct _MMBearer {
MmGdbusBearerSkeleton parent;
MMBearerPrivate *priv;
@@ -92,8 +100,9 @@ struct _MMBearerClass {
GAsyncResult *res,
GError **error);
- /* Report disconnection */
- void (* report_disconnection) (MMBearer *bearer);
+ /* Report connection status of this bearer */
+ void (* report_connection_status) (MMBearer *bearer,
+ MMBearerConnectionStatus status);
};
GType mm_bearer_get_type (void);
@@ -123,6 +132,7 @@ gboolean mm_bearer_disconnect_finish (MMBearer *self,
void mm_bearer_disconnect_force (MMBearer *self);
-void mm_bearer_report_disconnection (MMBearer *self);
+void mm_bearer_report_connection_status (MMBearer *self,
+ MMBearerConnectionStatus status);
#endif /* MM_BEARER_H */
diff --git a/src/mm-broadband-bearer.c b/src/mm-broadband-bearer.c
index f8f449d..33b4ca7 100644
--- a/src/mm-broadband-bearer.c
+++ b/src/mm-broadband-bearer.c
@@ -1785,13 +1785,17 @@ disconnect (MMBearer *self,
/*****************************************************************************/
static void
-report_disconnection (MMBearer *self)
+report_connection_status (MMBearer *self,
+ MMBearerConnectionStatus status)
{
- /* Cleanup all connection related data */
- reset_bearer_connection (MM_BROADBAND_BEARER (self));
+ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED)
+ /* Cleanup all connection related data */
+ reset_bearer_connection (MM_BROADBAND_BEARER (self));
- /* Chain up parent's report_disconection() */
- MM_BEARER_CLASS (mm_broadband_bearer_parent_class)->report_disconnection (self);
+ /* Chain up parent's report_connection_status() */
+ MM_BEARER_CLASS (mm_broadband_bearer_parent_class)->report_connection_status (
+ self,
+ status);
}
/*****************************************************************************/
@@ -2052,7 +2056,7 @@ mm_broadband_bearer_class_init (MMBroadbandBearerClass *klass)
bearer_class->connect_finish = connect_finish;
bearer_class->disconnect = disconnect;
bearer_class->disconnect_finish = disconnect_finish;
- bearer_class->report_disconnection = report_disconnection;
+ bearer_class->report_connection_status = report_connection_status;
klass->connect_3gpp = connect_3gpp;
klass->connect_3gpp_finish = detailed_connect_finish;
diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c
index f7dbd9b..774209e 100644
--- a/src/mm-broadband-modem-mbim.c
+++ b/src/mm-broadband-modem-mbim.c
@@ -36,6 +36,7 @@
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
#include "mm-iface-modem-messaging.h"
+#include "mm-sms-part-3gpp.h"
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
@@ -260,7 +261,7 @@ modem_load_model (MMIfaceModem *self,
result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
- modem_load_manufacturer);
+ modem_load_model);
g_simple_async_result_complete_in_idle (result);
g_object_unref (result);
}
@@ -1486,7 +1487,7 @@ modem_3gpp_load_enabled_facility_locks (MMIfaceModem3gpp *self,
result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
- modem_load_unlock_retries);
+ modem_3gpp_load_enabled_facility_locks);
message = mbim_message_pin_list_query_new (NULL);
mbim_device_command (device,
@@ -2276,7 +2277,7 @@ modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
- modem_3gpp_run_registration_checks);
+ modem_3gpp_register_in_network);
if (operator_id && operator_id[0])
message = (mbim_message_register_state_set_new (
@@ -2300,6 +2301,81 @@ modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
}
/*****************************************************************************/
+/* Scan networks (3GPP interface) */
+
+static GList *
+modem_3gpp_scan_networks_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ MbimMessage *response;
+ MbimProvider **providers;
+ guint n_providers;
+ GList *info_list = NULL;
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return NULL;
+
+ response = (MbimMessage *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ if (mbim_message_command_done_get_result (response, error) &&
+ mbim_message_visible_providers_response_parse (response,
+ &n_providers,
+ &providers,
+ error)) {
+ info_list = mm_3gpp_network_info_list_from_mbim_providers ((const MbimProvider *const *)providers,
+ n_providers);
+ mbim_provider_array_free (providers);
+ }
+ return info_list;
+}
+
+static void
+visible_providers_query_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GSimpleAsyncResult *simple)
+{
+ MbimMessage *response;
+ GError *error = NULL;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (response)
+ g_simple_async_result_set_op_res_gpointer (simple, response, (GDestroyNotify)mbim_message_unref);
+ else
+ g_simple_async_result_take_error (simple, error);
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+modem_3gpp_scan_networks (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ MbimDevice *device;
+ MbimMessage *message;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ modem_3gpp_scan_networks);
+
+ mm_dbg ("scanning networks...");
+ message = mbim_message_visible_providers_query_new (MBIM_VISIBLE_PROVIDERS_ACTION_FULL_SCAN, NULL);
+ mbim_device_command (device,
+ message,
+ 120,
+ NULL,
+ (GAsyncReadyCallback)visible_providers_query_ready,
+ result);
+ mbim_message_unref (message);
+}
+
+/*****************************************************************************/
/* Check support (Messaging interface) */
static gboolean
@@ -2407,10 +2483,10 @@ add_sms_part (MMBroadbandModemMbim *self,
MMSmsPart *part;
GError *error = NULL;
- part = mm_sms_part_new_from_binary_pdu (pdu->message_index,
- pdu->pdu_data,
- pdu->pdu_data_size,
- &error);
+ part = mm_sms_part_3gpp_new_from_binary_pdu (pdu->message_index,
+ pdu->pdu_data,
+ pdu->pdu_data_size,
+ &error);
if (part) {
mm_dbg ("Correctly parsed PDU (%d)", pdu->message_index);
mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self),
@@ -2697,10 +2773,8 @@ iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
iface->run_registration_checks_finish = modem_3gpp_run_registration_checks_finish;
iface->register_in_network = modem_3gpp_register_in_network;
iface->register_in_network_finish = modem_3gpp_register_in_network_finish;
-
- /* TODO: use MBIM_CID_VISIBLE_PROVIDERS */
- iface->scan_networks = NULL;
- iface->scan_networks_finish = NULL;
+ iface->scan_networks = modem_3gpp_scan_networks;
+ iface->scan_networks_finish = modem_3gpp_scan_networks_finish;
}
static void
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c
index e7969ef..a396a96 100644
--- a/src/mm-broadband-modem-qmi.c
+++ b/src/mm-broadband-modem-qmi.c
@@ -35,9 +35,13 @@
#include "mm-iface-modem-messaging.h"
#include "mm-iface-modem-location.h"
#include "mm-iface-modem-firmware.h"
+#include "mm-iface-modem-signal.h"
+#include "mm-iface-modem-oma.h"
#include "mm-sim-qmi.h"
#include "mm-bearer-qmi.h"
#include "mm-sms-qmi.h"
+#include "mm-sms-part-3gpp.h"
+#include "mm-sms-part-cdma.h"
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
@@ -45,7 +49,9 @@ static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface);
static void iface_modem_cdma_init (MMIfaceModemCdma *iface);
static void iface_modem_messaging_init (MMIfaceModemMessaging *iface);
static void iface_modem_location_init (MMIfaceModemLocation *iface);
+static void iface_modem_oma_init (MMIfaceModemOma *iface);
static void iface_modem_firmware_init (MMIfaceModemFirmware *iface);
+static void iface_modem_signal_init (MMIfaceModemSignal *iface);
static MMIfaceModemMessaging *iface_modem_messaging_parent;
static MMIfaceModemLocation *iface_modem_location_parent;
@@ -57,6 +63,8 @@ G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQmi, mm_broadband_modem_qmi, MM_TYPE_BRO
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIGNAL, iface_modem_signal_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_OMA, iface_modem_oma_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init))
struct _MMBroadbandModemQmiPrivate {
@@ -105,6 +113,11 @@ struct _MMBroadbandModemQmiPrivate {
MMModemLocationSource enabled_sources;
guint location_event_report_indication_id;
+ /* Oma helpers */
+ gboolean oma_unsolicited_events_enabled;
+ gboolean oma_unsolicited_events_setup;
+ guint oma_event_report_indication_id;
+
/* Firmware helpers */
GList *firmware_list;
MMFirmwareProperties *current_firmware;
@@ -1431,21 +1444,31 @@ dms_uim_get_pin_status_ready (QmiClientDms *client,
g_prefix_error (&error, "QMI operation failed: ");
g_simple_async_result_take_error (simple, error);
} else if (!qmi_message_dms_uim_get_pin_status_output_get_result (output, &error)) {
- /* When no SIM inserted, an internal error when checking PIN status
- * needs to be fatal so that we mark the modem unusable. */
+ /* Fatal, so that we mark the modem unusable.*/
if (g_error_matches (error,
QMI_PROTOCOL_ERROR,
- QMI_PROTOCOL_ERROR_INTERNAL) ||
- g_error_matches (error,
- QMI_PROTOCOL_ERROR,
QMI_PROTOCOL_ERROR_UIM_UNINITIALIZED)) {
+ /* This error won't force a pin check retry */
g_simple_async_result_set_error (simple,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE,
- "Couldn't get PIN status: %s",
+ "SIM failure: %s",
error->message);
g_error_free (error);
- } else {
+ }
+ /* Internal errors are retry-able before being fatal */
+ else if (g_error_matches (error,
+ QMI_PROTOCOL_ERROR,
+ QMI_PROTOCOL_ERROR_INTERNAL)) {
+ g_simple_async_result_set_error (simple,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_RETRY,
+ "Couldn't get PIN status (retry): %s",
+ error->message);
+ g_error_free (error);
+ }
+ /* Other errors, just propagate them */
+ else {
g_prefix_error (&error, "Couldn't get PIN status: ");
g_simple_async_result_take_error (simple, error);
}
@@ -1855,7 +1878,7 @@ modem_load_current_bands (MMIfaceModem *self,
#if defined WITH_NEWEST_QMI_COMMANDS
/* Introduced in NAS 1.19 */
- if (qmi_client_check_version (ctx->client, 1, 19)) {
+ if (qmi_client_check_version (client, 1, 19)) {
qmi_client_nas_get_rf_band_information (QMI_CLIENT_NAS (client),
NULL,
5,
@@ -2267,7 +2290,7 @@ get_signal_info_ready (QmiClientNas *client,
{
QmiMessageNasGetSignalInfoOutput *output;
GError *error = NULL;
- guint quality = 0;
+ gint8 quality = 0;
output = qmi_client_nas_get_signal_info_finish (client, res, &error);
if (!output) {
@@ -4156,7 +4179,7 @@ process_gsm_info (QmiMessageNasGetSystemInfoOutput *response_output,
&cid_valid, &cid,
NULL, NULL, NULL, /* registration_reject_info */
&network_id_valid, &mcc, &mnc,
- &egprs_support_valid, &egprs_support,
+ NULL, NULL, /* egprs support */
NULL, NULL, /* dtm_support */
NULL)) {
mm_dbg ("No GSM service reported");
@@ -4180,7 +4203,7 @@ process_gsm_info (QmiMessageNasGetSystemInfoOutput *response_output,
&cid_valid, &cid,
NULL, NULL, NULL, /* registration_reject_info */
&network_id_valid, &mcc, &mnc,
- &egprs_support_valid, &egprs_support,
+ NULL, NULL, /* egprs support */
NULL, NULL, /* dtm_support */
NULL)) {
mm_dbg ("No GSM service reported");
@@ -5113,7 +5136,9 @@ modem_cdma_load_activation_state (MMIfaceModemCdma *self,
}
/*****************************************************************************/
-/* OTA activation (CDMA interface) */
+/* Manual and OTA Activation (CDMA interface) */
+
+#define MAX_MDN_CHECK_RETRIES 10
typedef enum {
CDMA_ACTIVATION_STEP_FIRST,
@@ -5129,7 +5154,15 @@ typedef struct {
QmiClientDms *client;
GSimpleAsyncResult *result;
CdmaActivationStep step;
- gchar *carrier_code;
+ /* OTA activation... */
+ QmiMessageDmsActivateAutomaticInput *input_automatic;
+ /* Manual activation... */
+ QmiMessageDmsActivateManualInput *input_manual;
+ guint total_segments_size;
+ guint segment_i;
+ guint n_segments;
+ GArray **segments;
+ guint n_mdn_check_retries;
} CdmaActivationContext;
static void
@@ -5138,11 +5171,18 @@ cdma_activation_context_complete_and_free (CdmaActivationContext *ctx)
/* Cleanup the activation context from the private info */
ctx->self->priv->activation_ctx = NULL;
+ for (ctx->segment_i = 0; ctx->segment_i < ctx->n_segments; ctx->segment_i++)
+ g_array_unref (ctx->segments[ctx->segment_i]);
+ g_free (ctx->segments);
+
+ if (ctx->input_automatic)
+ qmi_message_dms_activate_automatic_input_unref (ctx->input_automatic);
+ if (ctx->input_manual)
+ qmi_message_dms_activate_manual_input_unref (ctx->input_manual);
g_simple_async_result_complete (ctx->result);
g_object_unref (ctx->result);
g_object_unref (ctx->client);
g_object_unref (ctx->self);
- g_free (ctx->carrier_code);
g_slice_free (CdmaActivationContext, ctx);
}
@@ -5154,6 +5194,14 @@ modem_cdma_activate_finish (MMIfaceModemCdma *self,
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
+static gboolean
+modem_cdma_activate_manual_finish (MMIfaceModemCdma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
static void cdma_activation_context_step (CdmaActivationContext *ctx);
static void
@@ -5191,6 +5239,61 @@ activation_power_cycle_ready (MMBroadbandModemQmi *self,
cdma_activation_context_step (ctx);
}
+static gboolean
+retry_msisdn_check_cb (CdmaActivationContext *ctx)
+{
+ cdma_activation_context_step (ctx);
+ return FALSE;
+}
+
+static void
+activate_manual_get_msisdn_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ CdmaActivationContext *ctx)
+{
+ QmiMessageDmsGetMsisdnOutput *output = NULL;
+ GError *error = NULL;
+ const gchar *current_mdn = NULL;
+ const gchar *expected_mdn = NULL;
+
+ qmi_message_dms_activate_manual_input_get_info (ctx->input_manual,
+ NULL, /* spc */
+ NULL, /* sid */
+ &expected_mdn,
+ NULL, /* min */
+ NULL);
+
+ output = qmi_client_dms_get_msisdn_finish (client, res, &error);
+ if (output &&
+ qmi_message_dms_get_msisdn_output_get_result (output, NULL) &&
+ qmi_message_dms_get_msisdn_output_get_msisdn (output, &current_mdn, NULL) &&
+ g_str_equal (current_mdn, expected_mdn)) {
+ mm_dbg ("MDN successfully updated to '%s'", expected_mdn);
+ qmi_message_dms_get_msisdn_output_unref (output);
+ /* And go on to next step */
+ ctx->step++;
+ cdma_activation_context_step (ctx);
+ return;
+ }
+
+ if (output)
+ qmi_message_dms_get_msisdn_output_unref (output);
+
+ if (ctx->n_mdn_check_retries < MAX_MDN_CHECK_RETRIES) {
+ /* Retry after some time */
+ mm_dbg ("MDN not yet updated, retrying...");
+ g_timeout_add (1, (GSourceFunc) retry_msisdn_check_cb, ctx);
+ return;
+ }
+
+ /* Well, all retries consumed already, return error */
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "MDN was not correctly set during manual activation");
+ cdma_activation_context_complete_and_free (ctx);
+}
+
static void
activation_event_report_indication_cb (QmiClientDms *client,
QmiIndicationDmsEventReportOutput *output,
@@ -5287,6 +5390,47 @@ activate_automatic_ready (QmiClientDms *client,
}
static void
+activate_manual_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ CdmaActivationContext *ctx)
+{
+ QmiMessageDmsActivateManualOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_dms_activate_manual_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_simple_async_result_take_error (ctx->result, error);
+ cdma_activation_context_complete_and_free (ctx);
+ return;
+ }
+
+ if (!qmi_message_dms_activate_manual_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't request manual activation: ");
+ g_simple_async_result_take_error (ctx->result, error);
+ qmi_message_dms_activate_manual_output_unref (output);
+ cdma_activation_context_complete_and_free (ctx);
+ return;
+ }
+
+ qmi_message_dms_activate_manual_output_unref (output);
+
+ /* If pending segments to send, re-run same step */
+ if (ctx->n_segments) {
+ ctx->segment_i++;
+ if (ctx->segment_i < ctx->n_segments) {
+ /* There's a pending segment */
+ cdma_activation_context_step (ctx);
+ return;
+ }
+ }
+
+ /* No more segments to send, go on */
+ ctx->step++;
+ cdma_activation_context_step (ctx);
+}
+
+static void
ser_activation_state_ready (QmiClientDms *client,
GAsyncResult *res,
CdmaActivationContext *ctx)
@@ -5336,54 +5480,98 @@ cdma_activation_context_step (CdmaActivationContext *ctx)
ctx->step++;
/* Fall down to next step */
- case CDMA_ACTIVATION_STEP_ENABLE_INDICATIONS: {
- QmiMessageDmsSetEventReportInput *input;
+ case CDMA_ACTIVATION_STEP_ENABLE_INDICATIONS:
+ /* Indications needed in automatic activation */
+ if (ctx->input_automatic) {
+ QmiMessageDmsSetEventReportInput *input;
- mm_info ("Automatic activation step [1/5]: enabling indications");
+ mm_info ("Activation step [1/5]: enabling indications");
- input = qmi_message_dms_set_event_report_input_new ();
- qmi_message_dms_set_event_report_input_set_activation_state_reporting (input, TRUE, NULL);
- qmi_client_dms_set_event_report (
- ctx->client,
- input,
- 5,
- NULL,
- (GAsyncReadyCallback)ser_activation_state_ready,
- ctx);
- qmi_message_dms_set_event_report_input_unref (input);
- return;
- }
+ input = qmi_message_dms_set_event_report_input_new ();
+ qmi_message_dms_set_event_report_input_set_activation_state_reporting (input, TRUE, NULL);
+ qmi_client_dms_set_event_report (
+ ctx->client,
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)ser_activation_state_ready,
+ ctx);
+ qmi_message_dms_set_event_report_input_unref (input);
+ return;
+ }
- case CDMA_ACTIVATION_STEP_REQUEST_ACTIVATION: {
- QmiMessageDmsActivateAutomaticInput *input;
+ /* Manual activation, no indications needed */
+ g_assert (ctx->input_manual != NULL);
+ mm_info ("Activation step [1/5]: indications not needed in manual activation");
+ ctx->step++;
+ /* Fall down to next step */
- mm_info ("Automatic activation step [2/5]: requesting activation");
+ case CDMA_ACTIVATION_STEP_REQUEST_ACTIVATION:
+ /* Automatic activation */
+ if (ctx->input_automatic) {
+ mm_info ("Activation step [2/5]: requesting automatic (OTA) activation");
+
+ qmi_client_dms_activate_automatic (ctx->client,
+ ctx->input_automatic,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)activate_automatic_ready,
+ ctx);
+ return;
+ }
+
+ /* Manual activation */
+ g_assert (ctx->input_manual != NULL);
+ if (!ctx->segments)
+ mm_info ("Activation step [2/5]: requesting manual activation");
+ else {
+ mm_info ("Activation step [2/5]: requesting manual activation (PRL segment %u/%u)",
+ (ctx->segment_i + 1), ctx->n_segments);
+ qmi_message_dms_activate_manual_input_set_prl (
+ ctx->input_manual,
+ (guint16)ctx->total_segments_size,
+ (guint8)ctx->segment_i,
+ ctx->segments[ctx->segment_i],
+ NULL);
+ }
- input = qmi_message_dms_activate_automatic_input_new ();
- qmi_message_dms_activate_automatic_input_set_activation_code (input, ctx->carrier_code, NULL);
- qmi_client_dms_activate_automatic (ctx->client,
- input,
- 10,
- NULL,
- (GAsyncReadyCallback)activate_automatic_ready,
- ctx);
- qmi_message_dms_activate_automatic_input_unref (input);
+ qmi_client_dms_activate_manual (ctx->client,
+ ctx->input_manual,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)activate_manual_ready,
+ ctx);
return;
- }
case CDMA_ACTIVATION_STEP_WAIT_UNTIL_FINISHED:
- mm_info ("Automatic activation step [3/5]: waiting for activation state updates");
+ /* Automatic activation */
+ if (ctx->input_automatic) {
+ /* State updates via unsolicited messages */
+ mm_info ("Activation step [3/5]: waiting for activation state updates");
+ return;
+ }
+
+ /* Manual activation; needs MSISDN checks */
+ g_assert (ctx->input_manual != NULL);
+ ctx->n_mdn_check_retries++;
+ mm_info ("Activation step [3/5]: checking MDN update (retry %u)", ctx->n_mdn_check_retries);
+ qmi_client_dms_get_msisdn (ctx->client,
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)activate_manual_get_msisdn_ready,
+ ctx);
return;
case CDMA_ACTIVATION_STEP_POWER_CYCLE:
- mm_info ("Automatic activation step [4/5]: power-cycling...");
+ mm_info ("Activation step [4/5]: power-cycling...");
power_cycle (ctx->self,
(GAsyncReadyCallback)activation_power_cycle_ready,
ctx);
return;
case CDMA_ACTIVATION_STEP_LAST:
- mm_info ("Automatic activation step [5/5]: finished");
+ mm_info ("Activation step [5/5]: finished");
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
cdma_activation_context_complete_and_free (ctx);
return;
@@ -5431,12 +5619,126 @@ modem_cdma_activate (MMIfaceModemCdma *_self,
ctx->self = g_object_ref (self);
ctx->client = g_object_ref (client);
ctx->result = result;
- ctx->carrier_code = g_strdup (carrier_code);
ctx->step = CDMA_ACTIVATION_STEP_FIRST;
+ /* Build base input bundle for the Automatic activation */
+ ctx->input_automatic = qmi_message_dms_activate_automatic_input_new ();
+ qmi_message_dms_activate_automatic_input_set_activation_code (ctx->input_automatic, carrier_code, NULL);
+
+ /* We keep the activation context in the private data, so that we don't
+ * allow multiple activation requests at the same time. */
+ self->priv->activation_ctx = ctx;
+ cdma_activation_context_step (ctx);
+}
+
+static void
+modem_cdma_activate_manual (MMIfaceModemCdma *_self,
+ MMCdmaManualActivationProperties *properties,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ GSimpleAsyncResult *result;
+ CdmaActivationContext *ctx;
+ QmiClient *client = NULL;
+
+ if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
+ return;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ modem_cdma_activate_manual);
+
+ /* Fail if we have already an activation ongoing */
+ if (self->priv->activation_ctx) {
+ g_simple_async_result_set_error (
+ result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_IN_PROGRESS,
+ "An activation operation is already in progress");
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+ return;
+ }
+
+ /* Setup context */
+ ctx = g_slice_new0 (CdmaActivationContext);
+ ctx->self = g_object_ref (self);
+ ctx->client = g_object_ref (client);
+ ctx->result = result;
+
/* We keep the activation context in the private data, so that we don't
* allow multiple activation requests at the same time. */
self->priv->activation_ctx = ctx;
+
+ /* Build base input bundle for the Manual activation */
+ ctx->input_manual = qmi_message_dms_activate_manual_input_new ();
+ qmi_message_dms_activate_manual_input_set_info (
+ ctx->input_manual,
+ mm_cdma_manual_activation_properties_get_spc (properties),
+ mm_cdma_manual_activation_properties_get_sid (properties),
+ mm_cdma_manual_activation_properties_get_mdn (properties),
+ mm_cdma_manual_activation_properties_get_min (properties),
+ NULL);
+
+ if (mm_cdma_manual_activation_properties_get_mn_ha_key (properties))
+ qmi_message_dms_activate_manual_input_set_mn_ha_key (
+ ctx->input_manual,
+ mm_cdma_manual_activation_properties_get_mn_ha_key (properties),
+ NULL);
+
+ if (mm_cdma_manual_activation_properties_get_mn_aaa_key (properties))
+ qmi_message_dms_activate_manual_input_set_mn_aaa_key (
+ ctx->input_manual,
+ mm_cdma_manual_activation_properties_get_mn_aaa_key (properties),
+ NULL);
+
+ if (mm_cdma_manual_activation_properties_peek_prl_bytearray (properties)) {
+ GByteArray *full_prl;
+ guint i;
+ guint adding;
+ guint remaining;
+
+ /* Just assume 512 is the max segment size...
+ * TODO: probably need to read max segment size from the usb descriptor
+ * WARN! Never ever use a MAX_PRL_SEGMENT_SIZE less than 64, or the sequence number
+ * won't fit in a single byte!!! (16384/256=64) */
+#define MAX_PRL_SEGMENT_SIZE 512
+
+ full_prl = mm_cdma_manual_activation_properties_peek_prl_bytearray (properties);
+
+ /* NOTE: max PRL size should already be checked when reading from DBus,
+ * so assert if longer */
+ ctx->total_segments_size = full_prl->len;
+ g_assert (ctx->total_segments_size <= 16384);
+
+ ctx->n_segments = (guint) (full_prl->len / MAX_PRL_SEGMENT_SIZE);
+ if (full_prl->len % MAX_PRL_SEGMENT_SIZE != 0)
+ ctx->n_segments++;
+ g_assert (ctx->n_segments <= 256);
+
+ ctx->segments = g_new0 (GArray *, (ctx->n_segments + 1));
+
+ adding = 0;
+ remaining = full_prl->len;
+ for (i = 0; i < ctx->n_segments; i++) {
+ guint current_add;
+
+ g_assert (remaining > 0);
+ current_add = remaining > MAX_PRL_SEGMENT_SIZE ? MAX_PRL_SEGMENT_SIZE : remaining;
+ ctx->segments[i] = g_array_sized_new (FALSE, FALSE, sizeof (guint8), current_add);
+ g_array_append_vals (ctx->segments[i], &(full_prl->data[adding]), current_add);
+ adding += current_add;
+ g_assert (remaining >= current_add);
+ remaining -= current_add;
+ }
+
+#undef MAX_PRL_SEGMENT_SIZE
+ }
+
cdma_activation_context_step (ctx);
}
@@ -6273,17 +6575,8 @@ messaging_check_support (MMIfaceModemMessaging *self,
return;
}
- /* We only handle 3GPP messaging (PDU based) currently, so just ignore
- * CDMA-only QMI modems */
- if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self))) {
- mm_dbg ("Messaging capabilities supported by this modem, "
- "but 3GPP2 messaging not supported yet by ModemManager");
- g_simple_async_result_set_op_res_gboolean (result, FALSE);
- } else {
- mm_dbg ("Messaging capabilities supported");
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- }
-
+ mm_dbg ("Messaging capabilities supported");
+ g_simple_async_result_set_op_res_gboolean (result, TRUE);
g_simple_async_result_complete_in_idle (result);
g_object_unref (result);
}
@@ -6308,8 +6601,11 @@ messaging_load_supported_storages_finish (MMIfaceModemMessaging *_self,
}
*mem1 = g_array_sized_new (FALSE, FALSE, sizeof (MMSmsStorage), 2);
- supported = MM_SMS_STORAGE_SM;
- g_array_append_val (*mem1, supported);
+ /* Add SM storage only if not CDMA-only */
+ if (!mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self))) {
+ supported = MM_SMS_STORAGE_SM;
+ g_array_append_val (*mem1, supported);
+ }
supported = MM_SMS_STORAGE_ME;
g_array_append_val (*mem1, supported);
*mem2 = g_array_ref (*mem1);
@@ -6484,11 +6780,20 @@ messaging_set_default_storage (MMIfaceModemMessaging *_self,
typedef enum {
LOAD_INITIAL_SMS_PARTS_STEP_FIRST,
- LOAD_INITIAL_SMS_PARTS_STEP_LIST_ALL,
- LOAD_INITIAL_SMS_PARTS_STEP_LIST_MT_READ,
- LOAD_INITIAL_SMS_PARTS_STEP_LIST_MT_NOT_READ,
- LOAD_INITIAL_SMS_PARTS_STEP_LIST_MO_SENT,
- LOAD_INITIAL_SMS_PARTS_STEP_LIST_MO_NOT_SENT,
+ LOAD_INITIAL_SMS_PARTS_STEP_3GPP_FIRST,
+ LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_ALL,
+ LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_MT_READ,
+ LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_MT_NOT_READ,
+ LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_MO_SENT,
+ LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_MO_NOT_SENT,
+ LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LAST,
+ LOAD_INITIAL_SMS_PARTS_STEP_CDMA_FIRST,
+ LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_ALL,
+ LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_MT_READ,
+ LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_MT_NOT_READ,
+ LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_MO_SENT,
+ LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_MO_NOT_SENT,
+ LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LAST,
LOAD_INITIAL_SMS_PARTS_STEP_LAST
} LoadInitialSmsPartsStep;
@@ -6543,43 +6848,43 @@ add_new_read_sms_part (MMIfaceModemMessaging *self,
QmiWmsMessageFormat format,
GArray *data)
{
+ MMSmsPart *part = NULL;
+ GError *error = NULL;
+
switch (format) {
case QMI_WMS_MESSAGE_FORMAT_CDMA:
- mm_dbg ("Skipping CDMA messages for now...");
+ part = mm_sms_part_cdma_new_from_binary_pdu (index,
+ (guint8 *)data->data,
+ data->len,
+ &error);
+
+ break;
+ case QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_POINT_TO_POINT:
+ case QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_BROADCAST:
+ part = mm_sms_part_3gpp_new_from_binary_pdu (index,
+ (guint8 *)data->data,
+ data->len,
+ &error);
break;
case QMI_WMS_MESSAGE_FORMAT_MWI:
mm_dbg ("Don't know how to process 'message waiting indicator' messages");
break;
- case QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_POINT_TO_POINT:
- case QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_BROADCAST: {
- MMSmsPart *part;
- GError *error = NULL;
-
- part = mm_sms_part_new_from_binary_pdu (index,
- (guint8 *)data->data,
- data->len,
- &error);
- if (part) {
- mm_dbg ("Correctly parsed PDU (%d)",
- index);
- mm_iface_modem_messaging_take_part (self,
- part,
- mm_sms_state_from_qmi_message_tag (tag),
- mm_sms_storage_from_qmi_storage_type (storage));
- } else {
- /* Don't treat the error as critical */
- mm_dbg ("Error parsing PDU (%d): %s",
- index,
- error->message);
- g_error_free (error);
- }
-
- break;
- }
default:
mm_dbg ("Unhandled message format '%u'", format);
break;
}
+
+ if (part) {
+ mm_dbg ("Correctly parsed PDU (%d)", index);
+ mm_iface_modem_messaging_take_part (self,
+ part,
+ mm_sms_state_from_qmi_message_tag (tag),
+ mm_sms_storage_from_qmi_storage_type (storage));
+ } else if (error) {
+ /* Don't treat the error as critical */
+ mm_dbg ("Error parsing PDU (%d): %s", index, error->message);
+ g_error_free (error);
+ }
}
static void
@@ -6642,8 +6947,10 @@ read_next_sms_part (LoadInitialSmsPartsContext *ctx)
if (ctx->i >= ctx->message_array->len ||
!ctx->message_array) {
/* If we just listed all SMS, we're done. Otherwise go to next tag. */
- if (ctx->step == LOAD_INITIAL_SMS_PARTS_STEP_LIST_ALL)
- ctx->step = LOAD_INITIAL_SMS_PARTS_STEP_LAST;
+ if (ctx->step == LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_ALL)
+ ctx->step = LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LAST;
+ else if (ctx->step == LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_ALL)
+ ctx->step = LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LAST;
else
ctx->step++;
load_initial_sms_parts_step (ctx);
@@ -6660,11 +6967,21 @@ read_next_sms_part (LoadInitialSmsPartsContext *ctx)
mm_sms_storage_to_qmi_storage_type (ctx->storage),
message->memory_index,
NULL);
- /* Only reading 3GPP SMS for now */
- qmi_message_wms_raw_read_input_set_message_mode (
- input,
- QMI_WMS_MESSAGE_MODE_GSM_WCDMA,
- NULL);
+
+ /* set message mode */
+ if (ctx->step < LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LAST)
+ qmi_message_wms_raw_read_input_set_message_mode (
+ input,
+ QMI_WMS_MESSAGE_MODE_GSM_WCDMA,
+ NULL);
+ else if (ctx->step < LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LAST)
+ qmi_message_wms_raw_read_input_set_message_mode (
+ input,
+ QMI_WMS_MESSAGE_MODE_CDMA,
+ NULL);
+ else
+ g_assert_not_reached ();
+
qmi_client_wms_raw_read (QMI_CLIENT_WMS (ctx->client),
input,
3,
@@ -6719,36 +7036,95 @@ static void
load_initial_sms_parts_step (LoadInitialSmsPartsContext *ctx)
{
QmiMessageWmsListMessagesInput *input;
+ gint mode = -1;
gint tag_type = -1;
switch (ctx->step) {
case LOAD_INITIAL_SMS_PARTS_STEP_FIRST:
ctx->step++;
/* Fall down */
- case LOAD_INITIAL_SMS_PARTS_STEP_LIST_ALL:
- mm_dbg ("loading all messages from storage '%s'...",
+ case LOAD_INITIAL_SMS_PARTS_STEP_3GPP_FIRST:
+ /* If modem doesn't have 3GPP caps, skip 3GPP SMS */
+ if (!mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self))) {
+ ctx->step = LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LAST;
+ load_initial_sms_parts_step (ctx);
+ return;
+ }
+ ctx->step++;
+ /* Fall down */
+ case LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_ALL:
+ mm_dbg ("loading all 3GPP messages from storage '%s'...",
+ mm_sms_storage_get_string (ctx->storage));
+ mode = QMI_WMS_MESSAGE_MODE_GSM_WCDMA;
+ break;
+ case LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_MT_READ:
+ mm_dbg ("loading 3GPP MT-read messages from storage '%s'...",
+ mm_sms_storage_get_string (ctx->storage));
+ tag_type = QMI_WMS_MESSAGE_TAG_TYPE_MT_READ;
+ mode = QMI_WMS_MESSAGE_MODE_GSM_WCDMA;
+ break;
+ case LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_MT_NOT_READ:
+ mm_dbg ("loading 3GPP MT-not-read messages from storage '%s'...",
+ mm_sms_storage_get_string (ctx->storage));
+ tag_type = QMI_WMS_MESSAGE_TAG_TYPE_MT_NOT_READ;
+ mode = QMI_WMS_MESSAGE_MODE_GSM_WCDMA;
+ break;
+ case LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_MO_SENT:
+ mm_dbg ("loading 3GPP MO-sent messages from storage '%s'...",
+ mm_sms_storage_get_string (ctx->storage));
+ tag_type = QMI_WMS_MESSAGE_TAG_TYPE_MO_SENT;
+ mode = QMI_WMS_MESSAGE_MODE_GSM_WCDMA;
+ break;
+ case LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_MO_NOT_SENT:
+ mm_dbg ("loading 3GPP MO-not-sent messages from storage '%s'...",
+ mm_sms_storage_get_string (ctx->storage));
+ tag_type = QMI_WMS_MESSAGE_TAG_TYPE_MO_NOT_SENT;
+ mode = QMI_WMS_MESSAGE_MODE_GSM_WCDMA;
+ break;
+ case LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LAST:
+ ctx->step++;
+ /* Fall down */
+ case LOAD_INITIAL_SMS_PARTS_STEP_CDMA_FIRST:
+ /* If modem doesn't have CDMA caps, skip CDMA SMS */
+ if (!mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->self))) {
+ ctx->step = LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LAST;
+ load_initial_sms_parts_step (ctx);
+ return;
+ }
+ ctx->step++;
+ /* Fall down */
+ case LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_ALL:
+ mm_dbg ("loading all CDMA messages from storage '%s'...",
mm_sms_storage_get_string (ctx->storage));
+ mode = QMI_WMS_MESSAGE_MODE_CDMA;
break;
- case LOAD_INITIAL_SMS_PARTS_STEP_LIST_MT_READ:
- mm_dbg ("loading MT-read messages from storage '%s'...",
+ case LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_MT_READ:
+ mm_dbg ("loading CDMA MT-read messages from storage '%s'...",
mm_sms_storage_get_string (ctx->storage));
tag_type = QMI_WMS_MESSAGE_TAG_TYPE_MT_READ;
+ mode = QMI_WMS_MESSAGE_MODE_CDMA;
break;
- case LOAD_INITIAL_SMS_PARTS_STEP_LIST_MT_NOT_READ:
- mm_dbg ("loading MT-not-read messages from storage '%s'...",
+ case LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_MT_NOT_READ:
+ mm_dbg ("loading CDMA MT-not-read messages from storage '%s'...",
mm_sms_storage_get_string (ctx->storage));
tag_type = QMI_WMS_MESSAGE_TAG_TYPE_MT_NOT_READ;
+ mode = QMI_WMS_MESSAGE_MODE_CDMA;
break;
- case LOAD_INITIAL_SMS_PARTS_STEP_LIST_MO_SENT:
- mm_dbg ("loading MO-sent messages from storage '%s'...",
+ case LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_MO_SENT:
+ mm_dbg ("loading CDMA MO-sent messages from storage '%s'...",
mm_sms_storage_get_string (ctx->storage));
tag_type = QMI_WMS_MESSAGE_TAG_TYPE_MO_SENT;
+ mode = QMI_WMS_MESSAGE_MODE_CDMA;
break;
- case LOAD_INITIAL_SMS_PARTS_STEP_LIST_MO_NOT_SENT:
- mm_dbg ("loading MO-not-sent messages from storage '%s'...",
+ case LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_MO_NOT_SENT:
+ mm_dbg ("loading CDMA MO-not-sent messages from storage '%s'...",
mm_sms_storage_get_string (ctx->storage));
tag_type = QMI_WMS_MESSAGE_TAG_TYPE_MO_NOT_SENT;
+ mode = QMI_WMS_MESSAGE_MODE_CDMA;
break;
+ case LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LAST:
+ ctx->step++;
+ /* Fall down */
case LOAD_INITIAL_SMS_PARTS_STEP_LAST:
/* All steps done */
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
@@ -6756,7 +7132,7 @@ load_initial_sms_parts_step (LoadInitialSmsPartsContext *ctx)
return;
}
- /* Request to list messages in a given storage */
+ g_assert (mode != -1);
input = qmi_message_wms_list_messages_input_new ();
qmi_message_wms_list_messages_input_set_storage_type (
input,
@@ -6764,7 +7140,7 @@ load_initial_sms_parts_step (LoadInitialSmsPartsContext *ctx)
NULL);
qmi_message_wms_list_messages_input_set_message_mode (
input,
- QMI_WMS_MESSAGE_MODE_GSM_WCDMA,
+ (QmiWmsMessageMode)mode,
NULL);
if (tag_type != -1)
qmi_message_wms_list_messages_input_set_message_tag (
@@ -7071,7 +7447,7 @@ messaging_disable_unsolicited_events_finish (MMIfaceModemMessaging *_self,
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
/* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ if (self->priv->messaging_fallback_at && iface_modem_messaging_parent->disable_unsolicited_events_finish) {
return iface_modem_messaging_parent->disable_unsolicited_events_finish (_self, res, error);
}
@@ -7180,6 +7556,21 @@ messaging_disable_unsolicited_events (MMIfaceModemMessaging *_self,
/* Handle fallback */
if (self->priv->messaging_fallback_at) {
+ /* Generic implementation doesn't actually have a method to disable
+ * unsolicited messaging events */
+ if (!iface_modem_messaging_parent->disable_unsolicited_events) {
+ GSimpleAsyncResult *result;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ messaging_disable_unsolicited_events);
+ g_simple_async_result_set_op_res_gboolean (result, TRUE);
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+ return;
+ }
+
return iface_modem_messaging_parent->disable_unsolicited_events (_self, callback, user_data);
}
@@ -7708,6 +8099,659 @@ enable_location_gathering (MMIfaceModemLocation *self,
}
/*****************************************************************************/
+/* Check support (OMA interface) */
+
+static gboolean
+oma_check_support_finish (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ /* no error expected here */
+ return g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res));
+}
+
+static void
+oma_check_support (MMIfaceModemOma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ MMQmiPort *port;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ oma_check_support);
+
+ port = mm_base_modem_peek_port_qmi (MM_BASE_MODEM (self));
+ /* If we have support for the OMA client, OMA is supported */
+ if (!port || !mm_qmi_port_peek_client (port, QMI_SERVICE_OMA, MM_QMI_PORT_FLAG_DEFAULT)) {
+ mm_dbg ("OMA capabilities not supported");
+ g_simple_async_result_set_op_res_gboolean (result, FALSE);
+ } else {
+ mm_dbg ("OMA capabilities supported");
+ g_simple_async_result_set_op_res_gboolean (result, TRUE);
+ }
+
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+}
+
+/*****************************************************************************/
+/* Load features (OMA interface) */
+
+static MMOmaFeature
+oma_load_features_finish (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return MM_OMA_FEATURE_NONE;
+
+ return (MMOmaFeature) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+static void
+oma_get_feature_setting_ready (QmiClientOma *client,
+ GAsyncResult *res,
+ GSimpleAsyncResult *simple)
+{
+ QmiMessageOmaGetFeatureSettingOutput *output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_oma_get_feature_setting_finish (client, res, &error);
+ if (!output || !qmi_message_oma_get_feature_setting_output_get_result (output, &error))
+ g_simple_async_result_take_error (simple, error);
+ else {
+ MMOmaFeature features = MM_OMA_FEATURE_NONE;
+ gboolean enabled;
+
+ if (qmi_message_oma_get_feature_setting_output_get_device_provisioning_service_update_config (
+ output,
+ &enabled,
+ NULL) &&
+ enabled)
+ features |= MM_OMA_FEATURE_DEVICE_PROVISIONING;
+
+ if (qmi_message_oma_get_feature_setting_output_get_prl_update_service_config (
+ output,
+ &enabled,
+ NULL) &&
+ enabled)
+ features |= MM_OMA_FEATURE_PRL_UPDATE;
+
+ if (qmi_message_oma_get_feature_setting_output_get_hfa_feature_config (
+ output,
+ &enabled,
+ NULL) &&
+ enabled)
+ features |= MM_OMA_FEATURE_HANDS_FREE_ACTIVATION;
+
+ g_simple_async_result_set_op_res_gpointer (simple, GUINT_TO_POINTER ((guint)features), NULL);
+ }
+
+ if (output)
+ qmi_message_oma_get_feature_setting_output_unref (output);
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+oma_load_features (MMIfaceModemOma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiClient *client = NULL;
+
+ if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
+ QMI_SERVICE_OMA, &client,
+ callback, user_data))
+ return;
+
+ qmi_client_oma_get_feature_setting (
+ QMI_CLIENT_OMA (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)oma_get_feature_setting_ready,
+ g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ oma_load_features));
+}
+
+/*****************************************************************************/
+/* Setup (OMA interface) */
+
+static gboolean
+oma_setup_finish (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+oma_set_feature_setting_ready (QmiClientOma *client,
+ GAsyncResult *res,
+ GSimpleAsyncResult *simple)
+{
+ QmiMessageOmaSetFeatureSettingOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_oma_set_feature_setting_finish (client, res, &error);
+ if (!output || !qmi_message_oma_set_feature_setting_output_get_result (output, &error))
+ g_simple_async_result_take_error (simple, error);
+ else
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+
+ if (output)
+ qmi_message_oma_set_feature_setting_output_unref (output);
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+oma_setup (MMIfaceModemOma *self,
+ MMOmaFeature features,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiClient *client = NULL;
+ QmiMessageOmaSetFeatureSettingInput *input;
+
+ if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
+ QMI_SERVICE_OMA, &client,
+ callback, user_data))
+ return;
+
+ input = qmi_message_oma_set_feature_setting_input_new ();
+ qmi_message_oma_set_feature_setting_input_set_device_provisioning_service_update_config (
+ input,
+ !!(features & MM_OMA_FEATURE_DEVICE_PROVISIONING),
+ NULL);
+ qmi_message_oma_set_feature_setting_input_set_prl_update_service_config (
+ input,
+ !!(features & MM_OMA_FEATURE_PRL_UPDATE),
+ NULL);
+ qmi_message_oma_set_feature_setting_input_set_hfa_feature_config (
+ input,
+ !!(features & MM_OMA_FEATURE_HANDS_FREE_ACTIVATION),
+ NULL);
+
+ qmi_client_oma_set_feature_setting (
+ QMI_CLIENT_OMA (client),
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)oma_set_feature_setting_ready,
+ g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ oma_setup));
+
+ qmi_message_oma_set_feature_setting_input_unref (input);
+}
+
+/*****************************************************************************/
+/* Start client initiated session (OMA interface) */
+
+static gboolean
+oma_start_client_initiated_session_finish (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+oma_start_session_ready (QmiClientOma *client,
+ GAsyncResult *res,
+ GSimpleAsyncResult *simple)
+{
+ QmiMessageOmaStartSessionOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_oma_start_session_finish (client, res, &error);
+ if (!output || !qmi_message_oma_start_session_output_get_result (output, &error))
+ g_simple_async_result_take_error (simple, error);
+ else
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+
+ if (output)
+ qmi_message_oma_start_session_output_unref (output);
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+oma_start_client_initiated_session (MMIfaceModemOma *self,
+ MMOmaSessionType session_type,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiClient *client = NULL;
+ QmiMessageOmaStartSessionInput *input;
+
+ if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
+ QMI_SERVICE_OMA, &client,
+ callback, user_data))
+ return;
+
+ /* It's already checked in mm-iface-modem-oma; so just assert if this is not ok */
+ g_assert (session_type == MM_OMA_SESSION_TYPE_CLIENT_INITIATED_DEVICE_CONFIGURE ||
+ session_type == MM_OMA_SESSION_TYPE_CLIENT_INITIATED_PRL_UPDATE ||
+ session_type == MM_OMA_SESSION_TYPE_CLIENT_INITIATED_HANDS_FREE_ACTIVATION);
+
+ input = qmi_message_oma_start_session_input_new ();
+ qmi_message_oma_start_session_input_set_session_type (
+ input,
+ mm_oma_session_type_to_qmi_oma_session_type (session_type),
+ NULL);
+
+ qmi_client_oma_start_session (
+ QMI_CLIENT_OMA (client),
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)oma_start_session_ready,
+ g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ oma_start_client_initiated_session));
+
+ qmi_message_oma_start_session_input_unref (input);
+}
+
+/*****************************************************************************/
+/* Accept network initiated session (OMA interface) */
+
+static gboolean
+oma_accept_network_initiated_session_finish (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+oma_send_selection_ready (QmiClientOma *client,
+ GAsyncResult *res,
+ GSimpleAsyncResult *simple)
+{
+ QmiMessageOmaSendSelectionOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_oma_send_selection_finish (client, res, &error);
+ if (!output || !qmi_message_oma_send_selection_output_get_result (output, &error))
+ g_simple_async_result_take_error (simple, error);
+ else
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+
+ if (output)
+ qmi_message_oma_send_selection_output_unref (output);
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+oma_accept_network_initiated_session (MMIfaceModemOma *self,
+ guint session_id,
+ gboolean accept,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiClient *client = NULL;
+ QmiMessageOmaSendSelectionInput *input;
+
+ if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
+ QMI_SERVICE_OMA, &client,
+ callback, user_data))
+ return;
+
+ input = qmi_message_oma_send_selection_input_new ();
+ qmi_message_oma_send_selection_input_set_network_initiated_alert_selection (
+ input,
+ accept,
+ (guint16)session_id,
+ NULL);
+
+ qmi_client_oma_send_selection (
+ QMI_CLIENT_OMA (client),
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)oma_send_selection_ready,
+ g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ oma_accept_network_initiated_session));
+
+ qmi_message_oma_send_selection_input_unref (input);
+}
+
+/*****************************************************************************/
+/* Cancel session (OMA interface) */
+
+static gboolean
+oma_cancel_session_finish (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+oma_cancel_session_ready (QmiClientOma *client,
+ GAsyncResult *res,
+ GSimpleAsyncResult *simple)
+{
+ QmiMessageOmaCancelSessionOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_oma_cancel_session_finish (client, res, &error);
+ if (!output || !qmi_message_oma_cancel_session_output_get_result (output, &error))
+ g_simple_async_result_take_error (simple, error);
+ else
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+
+ if (output)
+ qmi_message_oma_cancel_session_output_unref (output);
+
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+oma_cancel_session (MMIfaceModemOma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiClient *client = NULL;
+
+ if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
+ QMI_SERVICE_OMA, &client,
+ callback, user_data))
+ return;
+
+ qmi_client_oma_cancel_session (
+ QMI_CLIENT_OMA (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)oma_cancel_session_ready,
+ g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ oma_cancel_session));
+}
+
+/*****************************************************************************/
+/* Setup/Cleanup unsolicited event handlers (OMA interface) */
+
+static void
+oma_event_report_indication_cb (QmiClientNas *client,
+ QmiIndicationOmaEventReportOutput *output,
+ MMBroadbandModemQmi *self)
+{
+ QmiOmaSessionState qmi_session_state;
+ QmiOmaSessionType network_initiated_alert_session_type;
+ guint16 network_initiated_alert_session_id;
+
+ /* Update session state? */
+ if (qmi_indication_oma_event_report_output_get_session_state (
+ output,
+ &qmi_session_state,
+ NULL)) {
+ QmiOmaSessionFailedReason qmi_oma_session_failed_reason = QMI_OMA_SESSION_FAILED_REASON_UNKNOWN;
+
+ if (qmi_session_state == QMI_OMA_SESSION_STATE_FAILED)
+ qmi_indication_oma_event_report_output_get_session_fail_reason (
+ output,
+ &qmi_oma_session_failed_reason,
+ NULL);
+
+ mm_iface_modem_oma_update_session_state (
+ MM_IFACE_MODEM_OMA (self),
+ mm_oma_session_state_from_qmi_oma_session_state (qmi_session_state),
+ mm_oma_session_state_failed_reason_from_qmi_oma_session_failed_reason (qmi_oma_session_failed_reason));
+ }
+
+ /* New network initiated session? */
+ if (qmi_indication_oma_event_report_output_get_network_initiated_alert (
+ output,
+ &network_initiated_alert_session_type,
+ &network_initiated_alert_session_id,
+ NULL)) {
+ MMOmaSessionType session_type;
+
+ session_type = mm_oma_session_type_from_qmi_oma_session_type (network_initiated_alert_session_type);
+ if (session_type == MM_OMA_SESSION_TYPE_UNKNOWN)
+ g_warning ("Unknown QMI OMA session type '%u'", network_initiated_alert_session_type);
+ else
+ mm_iface_modem_oma_add_pending_network_initiated_session (
+ MM_IFACE_MODEM_OMA (self),
+ session_type,
+ (guint)network_initiated_alert_session_id);
+ }
+}
+
+static gboolean
+common_oma_setup_cleanup_unsolicited_events_finish (MMIfaceModemOma *_self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+common_setup_cleanup_oma_unsolicited_events (MMBroadbandModemQmi *self,
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ QmiClient *client = NULL;
+
+ if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
+ QMI_SERVICE_OMA, &client,
+ callback, user_data))
+ return;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ common_setup_cleanup_oma_unsolicited_events);
+
+ if (enable == self->priv->oma_unsolicited_events_setup) {
+ mm_dbg ("OMA unsolicited events already %s; skipping",
+ enable ? "setup" : "cleanup");
+ g_simple_async_result_set_op_res_gboolean (result, TRUE);
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+ return;
+ }
+
+ /* Store new state */
+ self->priv->oma_unsolicited_events_setup = enable;
+
+ /* Connect/Disconnect "Event Report" indications */
+ if (enable) {
+ g_assert (self->priv->oma_event_report_indication_id == 0);
+ self->priv->oma_event_report_indication_id =
+ g_signal_connect (client,
+ "event-report",
+ G_CALLBACK (oma_event_report_indication_cb),
+ self);
+ } else {
+ g_assert (self->priv->oma_event_report_indication_id != 0);
+ g_signal_handler_disconnect (client, self->priv->oma_event_report_indication_id);
+ self->priv->oma_event_report_indication_id = 0;
+ }
+
+ g_simple_async_result_set_op_res_gboolean (result, TRUE);
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+}
+
+static void
+oma_cleanup_unsolicited_events (MMIfaceModemOma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_setup_cleanup_oma_unsolicited_events (MM_BROADBAND_MODEM_QMI (self),
+ FALSE,
+ callback,
+ user_data);
+}
+
+static void
+oma_setup_unsolicited_events (MMIfaceModemOma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_setup_cleanup_oma_unsolicited_events (MM_BROADBAND_MODEM_QMI (self),
+ TRUE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Enable/Disable unsolicited events (OMA interface) */
+
+typedef struct {
+ MMBroadbandModemQmi *self;
+ GSimpleAsyncResult *result;
+ QmiClientOma *client;
+ gboolean enable;
+} EnableOmaUnsolicitedEventsContext;
+
+static void
+enable_oma_unsolicited_events_context_complete_and_free (EnableOmaUnsolicitedEventsContext *ctx)
+{
+ g_simple_async_result_complete (ctx->result);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->client);
+ g_object_unref (ctx->self);
+ g_slice_free (EnableOmaUnsolicitedEventsContext, ctx);
+}
+
+static gboolean
+common_oma_enable_disable_unsolicited_events_finish (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+ser_oma_indicator_ready (QmiClientOma *client,
+ GAsyncResult *res,
+ EnableOmaUnsolicitedEventsContext *ctx)
+{
+ QmiMessageOmaSetEventReportOutput *output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_oma_set_event_report_finish (client, res, &error);
+ if (!output) {
+ mm_dbg ("QMI operation failed: '%s'", error->message);
+ g_error_free (error);
+ } else if (!qmi_message_oma_set_event_report_output_get_result (output, &error)) {
+ mm_dbg ("Couldn't set event report: '%s'", error->message);
+ g_error_free (error);
+ }
+
+ if (output)
+ qmi_message_oma_set_event_report_output_unref (output);
+
+ /* Just ignore errors for now */
+ ctx->self->priv->oma_unsolicited_events_enabled = ctx->enable;
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ enable_oma_unsolicited_events_context_complete_and_free (ctx);
+}
+
+static void
+common_enable_disable_oma_unsolicited_events (MMBroadbandModemQmi *self,
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ EnableOmaUnsolicitedEventsContext *ctx;
+ GSimpleAsyncResult *result;
+ QmiClient *client = NULL;
+ QmiMessageOmaSetEventReportInput *input;
+
+ if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
+ QMI_SERVICE_OMA, &client,
+ callback, user_data))
+ return;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ common_enable_disable_oma_unsolicited_events);
+
+ if (enable == self->priv->oma_unsolicited_events_enabled) {
+ mm_dbg ("OMA unsolicited events already %s; skipping",
+ enable ? "enabled" : "disabled");
+ g_simple_async_result_set_op_res_gboolean (result, TRUE);
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+ return;
+ }
+
+ ctx = g_slice_new0 (EnableOmaUnsolicitedEventsContext);
+ ctx->self = g_object_ref (self);
+ ctx->client = g_object_ref (client);
+ ctx->enable = enable;
+ ctx->result = result;
+
+ input = qmi_message_oma_set_event_report_input_new ();
+ qmi_message_oma_set_event_report_input_set_session_state_reporting (
+ input,
+ ctx->enable,
+ NULL);
+ qmi_message_oma_set_event_report_input_set_network_initiated_alert_reporting (
+ input,
+ ctx->enable,
+ NULL);
+ qmi_client_oma_set_event_report (
+ ctx->client,
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)ser_oma_indicator_ready,
+ ctx);
+ qmi_message_oma_set_event_report_input_unref (input);
+}
+
+static void
+oma_disable_unsolicited_events (MMIfaceModemOma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_enable_disable_oma_unsolicited_events (MM_BROADBAND_MODEM_QMI (self),
+ FALSE,
+ callback,
+ user_data);
+}
+
+static void
+oma_enable_unsolicited_events (MMIfaceModemOma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_enable_disable_oma_unsolicited_events (MM_BROADBAND_MODEM_QMI (self),
+ TRUE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
/* Check firmware support (Firmware interface) */
typedef struct {
@@ -8337,6 +9381,481 @@ firmware_change_current (MMIfaceModemFirmware *self,
}
/*****************************************************************************/
+/* Check support (Signal interface) */
+
+static gboolean
+signal_check_support_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error)
+{
+
+ return g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res));
+}
+
+static void
+signal_check_support (MMIfaceModemSignal *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ MMQmiPort *port;
+ gboolean supported = FALSE;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ signal_check_support);
+
+ port = mm_base_modem_peek_port_qmi (MM_BASE_MODEM (self));
+
+ /* If NAS service is available, assume either signal info or signal strength are supported */
+ if (port)
+ supported = !!mm_qmi_port_peek_client (port, QMI_SERVICE_NAS, MM_QMI_PORT_FLAG_DEFAULT);
+
+ mm_dbg ("Extended signal capabilities %ssupported", supported ? "" : "not ");
+ g_simple_async_result_set_op_res_gboolean (result, supported);
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+}
+
+/*****************************************************************************/
+/* Load extended signal information */
+
+typedef enum {
+ SIGNAL_LOAD_VALUES_STEP_SIGNAL_FIRST,
+ SIGNAL_LOAD_VALUES_STEP_SIGNAL_INFO,
+ SIGNAL_LOAD_VALUES_STEP_SIGNAL_STRENGTH,
+ SIGNAL_LOAD_VALUES_STEP_SIGNAL_LAST
+} SignalLoadValuesStep;
+
+typedef struct {
+ MMSignal *cdma;
+ MMSignal *evdo;
+ MMSignal *gsm;
+ MMSignal *umts;
+ MMSignal *lte;
+} SignalLoadValuesResult;
+
+typedef struct {
+ MMBroadbandModemQmi *self;
+ QmiClientNas *client;
+ GSimpleAsyncResult *result;
+ SignalLoadValuesStep step;
+ SignalLoadValuesResult *values_result;
+} SignalLoadValuesContext;
+
+static void
+signal_load_values_result_free (SignalLoadValuesResult *result)
+{
+ if (result->cdma)
+ g_object_unref (result->cdma);
+ if (result->evdo)
+ g_object_unref (result->evdo);
+ if (result->gsm)
+ g_object_unref (result->gsm);
+ if (result->umts)
+ g_object_unref (result->umts);
+ if (result->lte)
+ g_object_unref (result->lte);
+ g_slice_free (SignalLoadValuesResult, result);
+}
+
+static void
+signal_load_values_context_complete_and_free (SignalLoadValuesContext *ctx)
+{
+ g_simple_async_result_complete (ctx->result);
+ if (ctx->values_result)
+ signal_load_values_result_free (ctx->values_result);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->self);
+ g_slice_free (SignalLoadValuesContext, ctx);
+}
+
+static gdouble
+get_db_from_sinr_level (QmiNasEvdoSinrLevel level)
+{
+ switch (level) {
+ case QMI_NAS_EVDO_SINR_LEVEL_0: return -9.0;
+ case QMI_NAS_EVDO_SINR_LEVEL_1: return -6;
+ case QMI_NAS_EVDO_SINR_LEVEL_2: return -4.5;
+ case QMI_NAS_EVDO_SINR_LEVEL_3: return -3;
+ case QMI_NAS_EVDO_SINR_LEVEL_4: return -2;
+ case QMI_NAS_EVDO_SINR_LEVEL_5: return 1;
+ case QMI_NAS_EVDO_SINR_LEVEL_6: return 3;
+ case QMI_NAS_EVDO_SINR_LEVEL_7: return 6;
+ case QMI_NAS_EVDO_SINR_LEVEL_8: return +9;
+ default:
+ mm_warn ("Invalid SINR level '%u'", level);
+ return -G_MAXDOUBLE;
+ }
+}
+
+static gboolean
+signal_load_values_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ MMSignal **cdma,
+ MMSignal **evdo,
+ MMSignal **gsm,
+ MMSignal **umts,
+ MMSignal **lte,
+ GError **error)
+{
+ SignalLoadValuesResult *values_result;
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return FALSE;
+
+ values_result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ *cdma = values_result->cdma ? g_object_ref (values_result->cdma) : NULL;
+ *evdo = values_result->evdo ? g_object_ref (values_result->evdo) : NULL;
+ *gsm = values_result->gsm ? g_object_ref (values_result->gsm) : NULL;
+ *umts = values_result->umts ? g_object_ref (values_result->umts) : NULL;
+ *lte = values_result->lte ? g_object_ref (values_result->lte) : NULL;
+
+ return TRUE;
+}
+
+static void signal_load_values_context_step (SignalLoadValuesContext *ctx);
+
+static void
+signal_load_values_get_signal_strength_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ SignalLoadValuesContext *ctx)
+{
+ QmiMessageNasGetSignalStrengthOutput *output;
+ GArray *array;
+ gint32 aux_int32;
+ gint16 aux_int16;
+ gint8 aux_int8;
+ QmiNasRadioInterface radio_interface;
+ QmiNasEvdoSinrLevel sinr;
+
+ output = qmi_client_nas_get_signal_strength_finish (client, res, NULL);
+ if (!output || !qmi_message_nas_get_signal_strength_output_get_result (output, NULL)) {
+ /* No hard errors, go on to next step */
+ ctx->step++;
+ signal_load_values_context_step (ctx);
+ if (output)
+ qmi_message_nas_get_signal_strength_output_unref (output);
+ return;
+ }
+
+ /* Good, we have results */
+ ctx->values_result = g_slice_new0 (SignalLoadValuesResult);
+
+ /* RSSI
+ *
+ * We will assume that valid access technologies reported in this output
+ * are the ones which are listed in the RSSI output. If a given access tech
+ * is not given in this list, it will not be considered afterwards (e.g. if
+ * no EV-DO is given in the RSSI list, the SINR level won't be processed,
+ * even if the TLV is available.
+ */
+ if (qmi_message_nas_get_signal_strength_output_get_rssi_list (output, &array, NULL)) {
+ guint i;
+
+ for (i = 0; i < array->len; i++) {
+ QmiMessageNasGetSignalStrengthOutputRssiListElement *element;
+
+ element = &g_array_index (array, QmiMessageNasGetSignalStrengthOutputRssiListElement, i);
+
+ switch (element->radio_interface) {
+ case QMI_NAS_RADIO_INTERFACE_CDMA_1X:
+ if (!ctx->values_result->cdma)
+ ctx->values_result->cdma = mm_signal_new ();
+ mm_signal_set_rssi (ctx->values_result->cdma, (gdouble)element->rssi);
+ break;
+ case QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO:
+ if (!ctx->values_result->evdo)
+ ctx->values_result->evdo = mm_signal_new ();
+ mm_signal_set_rssi (ctx->values_result->evdo, (gdouble)element->rssi);
+ break;
+ case QMI_NAS_RADIO_INTERFACE_GSM:
+ if (!ctx->values_result->gsm)
+ ctx->values_result->gsm = mm_signal_new ();
+ mm_signal_set_rssi (ctx->values_result->gsm, (gdouble)element->rssi);
+ break;
+ case QMI_NAS_RADIO_INTERFACE_UMTS:
+ if (!ctx->values_result->umts)
+ ctx->values_result->umts = mm_signal_new ();
+ mm_signal_set_rssi (ctx->values_result->umts, (gdouble)element->rssi);
+ break;
+ case QMI_NAS_RADIO_INTERFACE_LTE:
+ if (!ctx->values_result->lte)
+ ctx->values_result->lte = mm_signal_new ();
+ mm_signal_set_rssi (ctx->values_result->lte, (gdouble)element->rssi);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /* ECIO (CDMA, EV-DO and UMTS) */
+ if (qmi_message_nas_get_signal_strength_output_get_ecio_list (output, &array, NULL)) {
+ guint i;
+
+ for (i = 0; i < array->len; i++) {
+ QmiMessageNasGetSignalStrengthOutputEcioListElement *element;
+
+ element = &g_array_index (array, QmiMessageNasGetSignalStrengthOutputEcioListElement, i);
+
+ switch (element->radio_interface) {
+ case QMI_NAS_RADIO_INTERFACE_CDMA_1X:
+ if (ctx->values_result->cdma)
+ mm_signal_set_ecio (ctx->values_result->cdma, ((gdouble)element->ecio) * (-0.5));
+ break;
+ case QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO:
+ if (ctx->values_result->evdo)
+ mm_signal_set_ecio (ctx->values_result->evdo, ((gdouble)element->ecio) * (-0.5));
+ break;
+ case QMI_NAS_RADIO_INTERFACE_UMTS:
+ if (ctx->values_result->umts)
+ mm_signal_set_ecio (ctx->values_result->umts, ((gdouble)element->ecio) * (-0.5));
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /* IO (EV-DO) */
+ if (qmi_message_nas_get_signal_strength_output_get_io (output, &aux_int32, NULL)) {
+ if (ctx->values_result->evdo)
+ mm_signal_set_io (ctx->values_result->evdo, (gdouble)aux_int32);
+ }
+
+ /* RSRP (LTE) */
+ if (qmi_message_nas_get_signal_strength_output_get_lte_rsrp (output, &aux_int16, NULL)) {
+ if (ctx->values_result->lte)
+ mm_signal_set_rsrp (ctx->values_result->lte, (gdouble)aux_int16);
+ }
+
+ /* RSRQ (LTE) */
+ if (qmi_message_nas_get_signal_strength_output_get_rsrq (output, &aux_int8, &radio_interface, NULL) &&
+ radio_interface == QMI_NAS_RADIO_INTERFACE_LTE) {
+ if (ctx->values_result->lte)
+ mm_signal_set_rsrq (ctx->values_result->lte, (gdouble)aux_int8);
+ }
+
+ /* SNR (LTE) */
+ if (qmi_message_nas_get_signal_strength_output_get_lte_snr (output, &aux_int16, NULL)) {
+ if (ctx->values_result->lte)
+ mm_signal_set_snr (ctx->values_result->lte, (0.1) * ((gdouble)aux_int16));
+ }
+
+ /* SINR (EV-DO) */
+ if (qmi_message_nas_get_signal_strength_output_get_sinr (output, &sinr, NULL)) {
+ if (ctx->values_result->evdo)
+ mm_signal_set_sinr (ctx->values_result->evdo, get_db_from_sinr_level (sinr));
+ }
+
+ qmi_message_nas_get_signal_strength_output_unref (output);
+
+ /* Go on */
+ ctx->step++;
+ signal_load_values_context_step (ctx);
+}
+
+static void
+signal_load_values_get_signal_info_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ SignalLoadValuesContext *ctx)
+{
+ QmiMessageNasGetSignalInfoOutput *output;
+ gint8 rssi;
+ gint16 ecio;
+ QmiNasEvdoSinrLevel sinr_level;
+ gint32 io;
+ gint8 rsrq;
+ gint16 rsrp;
+ gint16 snr;
+
+ output = qmi_client_nas_get_signal_info_finish (client, res, NULL);
+ if (!output || !qmi_message_nas_get_signal_info_output_get_result (output, NULL)) {
+ /* No hard errors, go on to next step */
+ ctx->step++;
+ signal_load_values_context_step (ctx);
+ if (output)
+ qmi_message_nas_get_signal_info_output_unref (output);
+ return;
+ }
+
+ /* Good, we have results */
+ ctx->values_result = g_slice_new0 (SignalLoadValuesResult);
+
+ /* CDMA */
+ if (qmi_message_nas_get_signal_info_output_get_cdma_signal_strength (output,
+ &rssi,
+ &ecio,
+ NULL)) {
+ ctx->values_result->cdma = mm_signal_new ();
+ mm_signal_set_rssi (ctx->values_result->cdma, (gdouble)rssi);
+ mm_signal_set_ecio (ctx->values_result->cdma, ((gdouble)ecio) * (-0.5));
+ }
+
+ /* HDR... */
+ if (qmi_message_nas_get_signal_info_output_get_hdr_signal_strength (output,
+ &rssi,
+ &ecio,
+ &sinr_level,
+ &io,
+ NULL)) {
+ ctx->values_result->evdo = mm_signal_new ();
+ mm_signal_set_rssi (ctx->values_result->evdo, (gdouble)rssi);
+ mm_signal_set_ecio (ctx->values_result->evdo, ((gdouble)ecio) * (-0.5));
+ mm_signal_set_sinr (ctx->values_result->evdo, get_db_from_sinr_level (sinr_level));
+ mm_signal_set_io (ctx->values_result->evdo, (gdouble)io);
+ }
+
+ /* GSM */
+ if (qmi_message_nas_get_signal_info_output_get_gsm_signal_strength (output,
+ &rssi,
+ NULL)) {
+ ctx->values_result->gsm = mm_signal_new ();
+ mm_signal_set_rssi (ctx->values_result->gsm, (gdouble)rssi);
+ }
+
+ /* WCDMA... */
+ if (qmi_message_nas_get_signal_info_output_get_wcdma_signal_strength (output,
+ &rssi,
+ &ecio,
+ NULL)) {
+ ctx->values_result->umts = mm_signal_new ();
+ mm_signal_set_rssi (ctx->values_result->umts, (gdouble)rssi);
+ mm_signal_set_ecio (ctx->values_result->umts, ((gdouble)ecio) * (-0.5));
+ }
+
+ /* LTE... */
+ if (qmi_message_nas_get_signal_info_output_get_lte_signal_strength (output,
+ &rssi,
+ &rsrq,
+ &rsrp,
+ &snr,
+ NULL)) {
+ ctx->values_result->lte = mm_signal_new ();
+ mm_signal_set_rssi (ctx->values_result->lte, (gdouble)rssi);
+ mm_signal_set_rsrq (ctx->values_result->lte, (gdouble)rsrq);
+ mm_signal_set_rsrp (ctx->values_result->lte, (gdouble)rsrp);
+ mm_signal_set_snr (ctx->values_result->lte, (0.1) * ((gdouble)snr));
+ }
+
+ qmi_message_nas_get_signal_info_output_unref (output);
+
+ /* Keep on */
+ ctx->step++;
+ signal_load_values_context_step (ctx);
+}
+
+static void
+signal_load_values_context_step (SignalLoadValuesContext *ctx)
+{
+
+#define VALUES_RESULT_LOADED(ctx) \
+ (ctx->values_result && \
+ (ctx->values_result->cdma || \
+ ctx->values_result->evdo || \
+ ctx->values_result->gsm || \
+ ctx->values_result->umts || \
+ ctx->values_result->lte))
+
+ switch (ctx->step) {
+ case SIGNAL_LOAD_VALUES_STEP_SIGNAL_FIRST:
+ ctx->step++;
+ /* Fall down */
+
+ case SIGNAL_LOAD_VALUES_STEP_SIGNAL_INFO:
+ if (qmi_client_check_version (QMI_CLIENT (ctx->client), 1, 8)) {
+ qmi_client_nas_get_signal_info (ctx->client,
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)signal_load_values_get_signal_info_ready,
+ ctx);
+ return;
+ }
+ ctx->step++;
+ /* Fall down */
+
+ case SIGNAL_LOAD_VALUES_STEP_SIGNAL_STRENGTH:
+ /* If already loaded with signal info, don't try signal strength */
+ if (!VALUES_RESULT_LOADED (ctx)) {
+ QmiMessageNasGetSignalStrengthInput *input;
+
+ input = qmi_message_nas_get_signal_strength_input_new ();
+ qmi_message_nas_get_signal_strength_input_set_request_mask (
+ input,
+ (QMI_NAS_SIGNAL_STRENGTH_REQUEST_RSSI |
+ QMI_NAS_SIGNAL_STRENGTH_REQUEST_ECIO |
+ QMI_NAS_SIGNAL_STRENGTH_REQUEST_IO |
+ QMI_NAS_SIGNAL_STRENGTH_REQUEST_SINR |
+ QMI_NAS_SIGNAL_STRENGTH_REQUEST_RSRQ |
+ QMI_NAS_SIGNAL_STRENGTH_REQUEST_LTE_SNR |
+ QMI_NAS_SIGNAL_STRENGTH_REQUEST_LTE_RSRP),
+ NULL);
+ qmi_client_nas_get_signal_strength (ctx->client,
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)signal_load_values_get_signal_strength_ready,
+ ctx);
+ qmi_message_nas_get_signal_strength_input_unref (input);
+ return;
+ }
+ ctx->step++;
+ /* Fall down */
+
+ case SIGNAL_LOAD_VALUES_STEP_SIGNAL_LAST:
+ /* If any result is set, succeed */
+ if (VALUES_RESULT_LOADED (ctx)) {
+ g_simple_async_result_set_op_res_gpointer (ctx->result,
+ ctx->values_result,
+ (GDestroyNotify)signal_load_values_result_free);
+ ctx->values_result = NULL;
+ } else {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "No way to load extended signal information");
+ }
+ signal_load_values_context_complete_and_free (ctx);
+ return;
+ }
+
+ g_assert_not_reached ();
+
+#undef VALUES_RESULT_LOADED
+}
+
+static void
+signal_load_values (MMIfaceModemSignal *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SignalLoadValuesContext *ctx;
+ QmiClient *client = NULL;
+
+ mm_dbg ("loading extended signal information...");
+
+ if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
+ return;
+
+ ctx = g_slice_new0 (SignalLoadValuesContext);
+ ctx->self = g_object_ref (self);
+ ctx->client = g_object_ref (client);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ signal_load_values);
+ ctx->step = SIGNAL_LOAD_VALUES_STEP_SIGNAL_FIRST;
+
+ signal_load_values_context_step (ctx);
+}
+
+/*****************************************************************************/
/* First enabling step */
static gboolean
@@ -8567,7 +10086,8 @@ initialization_started (MMBroadbandModem *self,
ctx->services[1] = QMI_SERVICE_NAS;
ctx->services[2] = QMI_SERVICE_WMS;
ctx->services[3] = QMI_SERVICE_PDS;
- ctx->services[4] = QMI_SERVICE_UNKNOWN;
+ ctx->services[4] = QMI_SERVICE_OMA;
+ ctx->services[5] = QMI_SERVICE_UNKNOWN;
/* Now open our QMI port */
mm_qmi_port_open (ctx->qmi,
@@ -8794,6 +10314,8 @@ iface_modem_cdma_init (MMIfaceModemCdma *iface)
iface->load_activation_state_finish = modem_cdma_load_activation_state_finish;
iface->activate = modem_cdma_activate;
iface->activate_finish = modem_cdma_activate_finish;
+ iface->activate_manual = modem_cdma_activate_manual;
+ iface->activate_manual_finish = modem_cdma_activate_manual_finish;
}
static void
@@ -8836,6 +10358,40 @@ iface_modem_location_init (MMIfaceModemLocation *iface)
}
static void
+iface_modem_signal_init (MMIfaceModemSignal *iface)
+{
+ iface->check_support = signal_check_support;
+ iface->check_support_finish = signal_check_support_finish;
+ iface->load_values = signal_load_values;
+ iface->load_values_finish = signal_load_values_finish;
+}
+
+static void
+iface_modem_oma_init (MMIfaceModemOma *iface)
+{
+ iface->check_support = oma_check_support;
+ iface->check_support_finish = oma_check_support_finish;
+ iface->load_features = oma_load_features;
+ iface->load_features_finish = oma_load_features_finish;
+ iface->setup = oma_setup;
+ iface->setup_finish = oma_setup_finish;
+ iface->start_client_initiated_session = oma_start_client_initiated_session;
+ iface->start_client_initiated_session_finish = oma_start_client_initiated_session_finish;
+ iface->accept_network_initiated_session = oma_accept_network_initiated_session;
+ iface->accept_network_initiated_session_finish = oma_accept_network_initiated_session_finish;
+ iface->cancel_session = oma_cancel_session;
+ iface->cancel_session_finish = oma_cancel_session_finish;
+ iface->setup_unsolicited_events = oma_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = common_oma_setup_cleanup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = oma_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = common_oma_setup_cleanup_unsolicited_events_finish;
+ iface->enable_unsolicited_events = oma_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = common_oma_enable_disable_unsolicited_events_finish;
+ iface->disable_unsolicited_events = oma_disable_unsolicited_events;
+ iface->disable_unsolicited_events_finish = common_oma_enable_disable_unsolicited_events_finish;
+}
+
+static void
iface_modem_firmware_init (MMIfaceModemFirmware *iface)
{
iface->check_support = firmware_check_support;
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
index 13fc377..8e111c9 100644
--- a/src/mm-broadband-modem.c
+++ b/src/mm-broadband-modem.c
@@ -37,9 +37,12 @@
#include "mm-iface-modem-messaging.h"
#include "mm-iface-modem-time.h"
#include "mm-iface-modem-firmware.h"
+#include "mm-iface-modem-signal.h"
+#include "mm-iface-modem-oma.h"
#include "mm-broadband-bearer.h"
#include "mm-bearer-list.h"
#include "mm-sms-list.h"
+#include "mm-sms-part-3gpp.h"
#include "mm-sim.h"
#include "mm-log.h"
#include "mm-modem-helpers.h"
@@ -56,6 +59,8 @@ static void iface_modem_simple_init (MMIfaceModemSimple *iface);
static void iface_modem_location_init (MMIfaceModemLocation *iface);
static void iface_modem_messaging_init (MMIfaceModemMessaging *iface);
static void iface_modem_time_init (MMIfaceModemTime *iface);
+static void iface_modem_signal_init (MMIfaceModemSignal *iface);
+static void iface_modem_oma_init (MMIfaceModemOma *iface);
static void iface_modem_firmware_init (MMIfaceModemFirmware *iface);
G_DEFINE_TYPE_EXTENDED (MMBroadbandModem, mm_broadband_modem, MM_TYPE_BASE_MODEM, 0,
@@ -67,6 +72,8 @@ G_DEFINE_TYPE_EXTENDED (MMBroadbandModem, mm_broadband_modem, MM_TYPE_BASE_MODEM
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIGNAL, iface_modem_signal_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_OMA, iface_modem_oma_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init))
enum {
@@ -79,6 +86,8 @@ enum {
PROP_MODEM_LOCATION_DBUS_SKELETON,
PROP_MODEM_MESSAGING_DBUS_SKELETON,
PROP_MODEM_TIME_DBUS_SKELETON,
+ PROP_MODEM_SIGNAL_DBUS_SKELETON,
+ PROP_MODEM_OMA_DBUS_SKELETON,
PROP_MODEM_FIRMWARE_DBUS_SKELETON,
PROP_MODEM_SIM,
PROP_MODEM_BEARER_LIST,
@@ -184,6 +193,14 @@ struct _MMBroadbandModemPrivate {
/* Properties */
GObject *modem_time_dbus_skeleton;
+ /*<--- Modem Signal interface --->*/
+ /* Properties */
+ GObject *modem_signal_dbus_skeleton;
+
+ /*<--- Modem OMA interface --->*/
+ /* Properties */
+ GObject *modem_oma_dbus_skeleton;
+
/*<--- Modem Firmware interface --->*/
/* Properties */
GObject *modem_firmware_dbus_skeleton;
@@ -275,7 +292,7 @@ modem_create_bearer (MMIfaceModem *self,
}
/*****************************************************************************/
-/* Create SIM (Modem inteface) */
+/* Create SIM (Modem interface) */
static MMSim *
modem_create_sim_finish (MMIfaceModem *self,
@@ -590,9 +607,12 @@ mode_pref_qcdm_ready (MMQcdmSerialPort *port,
err = qcdm_result_get_u8 (result, QCDM_CMD_NV_GET_MODE_PREF_ITEM_MODE_PREF, &pref);
if (err) {
mm_dbg ("Failed to read NV ModePref: %d", err);
+ qcdm_result_unref (result);
goto at_caps;
}
+ qcdm_result_unref (result);
+
/* Only parse explicit modes; for 'auto' just fall back to whatever
* the AT current capabilities probing figures out.
*/
@@ -1727,19 +1747,16 @@ signal_quality_csq_ready (MMBroadbandModem *self,
result_str = mm_strip_tag (result_str, "+CSQ:");
if (sscanf (result_str, "%d, %d", &quality, &ber)) {
- /* 99 means unknown */
if (quality == 99) {
- g_simple_async_result_take_error (
- ctx->result,
- mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK));
+ /* 99 means unknown, no service, etc */
+ quality = 0;
} else {
/* Normalize the quality */
quality = CLAMP (quality, 0, 31) * 100 / 31;
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- GUINT_TO_POINTER (quality),
- NULL);
}
-
+ g_simple_async_result_set_op_res_gpointer (ctx->result,
+ GUINT_TO_POINTER (quality),
+ NULL);
signal_quality_context_complete_and_free (ctx);
return;
}
@@ -1971,7 +1988,8 @@ modem_load_signal_quality (MMIfaceModem *self,
/* Check whether we can get a non-connected AT port */
ctx->port = (MMSerialPort *)mm_base_modem_get_best_at_port (MM_BASE_MODEM (self), &error);
if (ctx->port) {
- if (MM_BROADBAND_MODEM (self)->priv->modem_cind_supported)
+ if (MM_BROADBAND_MODEM (self)->priv->modem_cind_supported &&
+ CIND_INDICATOR_IS_VALID (MM_BROADBAND_MODEM (self)->priv->modem_cind_indicator_signal_quality))
signal_quality_cind (ctx);
else
signal_quality_csq (ctx);
@@ -3423,6 +3441,49 @@ modem_3gpp_load_operator_name (MMIfaceModem3gpp *self,
}
/*****************************************************************************/
+/* Subscription State Loading (3GPP interface) */
+
+static MMModem3gppSubscriptionState
+modem_3gpp_load_subscription_state_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN;
+
+ return (MMModem3gppSubscriptionState) GPOINTER_TO_UINT (
+ g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+static void
+modem_3gpp_load_subscription_state (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ modem_3gpp_load_subscription_state);
+
+ /* Reloading subscription state only occurs on a successfully registered
+ * modem. (Although the 3GPP interface does not reflect this until after
+ * loading operator information completes.)
+ * By default, we can assume that successful registration implies a
+ * provisioned SIM.
+ */
+ mm_dbg ("Load subscription state: Marking the SIM as provisioned.");
+ g_simple_async_result_set_op_res_gpointer (
+ result,
+ GUINT_TO_POINTER (MM_MODEM_3GPP_SUBSCRIPTION_STATE_PROVISIONED),
+ NULL);
+
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+}
+
+/*****************************************************************************/
/* Unsolicited registration messages handling (3GPP interface) */
static gboolean
@@ -4230,7 +4291,7 @@ unsolicited_registration_events_context_step (UnsolicitedRegistrationEventsConte
/* All done!
* If we have any error reported, we'll propagate it. EPS errors take
- * precendence over PS errors and PS errors take precendence over CS errors. */
+ * precedence over PS errors and PS errors take precedence over CS errors. */
if (ctx->eps_error) {
g_simple_async_result_take_error (ctx->result, ctx->eps_error);
ctx->eps_error = NULL;
@@ -5518,7 +5579,7 @@ sms_part_ready (MMBroadbandModem *self,
{
MMSmsPart *part;
gint rv, status, tpdu_len;
- gchar pdu[SMS_MAX_PDU_LEN + 1];
+ gchar pdu[MM_SMS_PART_3GPP_MAX_PDU_LEN + 1];
const gchar *response;
GError *error = NULL;
@@ -5536,7 +5597,7 @@ sms_part_ready (MMBroadbandModem *self,
return;
}
- rv = sscanf (response, "+CMGR: %d,,%d %" G_STRINGIFY (SMS_MAX_PDU_LEN) "s",
+ rv = sscanf (response, "+CMGR: %d,,%d %" G_STRINGIFY (MM_SMS_PART_3GPP_MAX_PDU_LEN) "s",
&status, &tpdu_len, pdu);
if (rv != 3) {
error = g_error_new (MM_CORE_ERROR,
@@ -5548,7 +5609,7 @@ sms_part_ready (MMBroadbandModem *self,
return;
}
- part = mm_sms_part_new_from_pdu (ctx->idx, pdu, &error);
+ part = mm_sms_part_3gpp_new_from_pdu (ctx->idx, pdu, &error);
if (part) {
mm_dbg ("Correctly parsed PDU (%d)", ctx->idx);
mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self),
@@ -5658,7 +5719,7 @@ cds_received (MMAtSerialPort *port,
if (!pdu)
return;
- part = mm_sms_part_new_from_pdu (SMS_PART_INVALID_INDEX, pdu, &error);
+ part = mm_sms_part_3gpp_new_from_pdu (SMS_PART_INVALID_INDEX, pdu, &error);
if (part) {
mm_dbg ("Correctly parsed non-stored PDU");
mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self),
@@ -6025,7 +6086,7 @@ sms_pdu_part_list_ready (MMBroadbandModem *self,
MM3gppPduInfo *info = l->data;
MMSmsPart *part;
- part = mm_sms_part_new_from_pdu (info->index, info->pdu, &error);
+ part = mm_sms_part_3gpp_new_from_pdu (info->index, info->pdu, &error);
if (part) {
mm_dbg ("Correctly parsed PDU (%d)", info->index);
mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self),
@@ -7395,10 +7456,10 @@ enable_location_gathering (MMIfaceModemLocation *self,
mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) {
/* Reload registration to get LAC/CI */
mm_iface_modem_3gpp_run_registration_checks (MM_IFACE_MODEM_3GPP (self), NULL, NULL);
- /* Reload operator to get MCC/MNC */
+ /* Reload registration information to get MCC/MNC */
if (MM_BROADBAND_MODEM (self)->priv->modem_3gpp_registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
MM_BROADBAND_MODEM (self)->priv->modem_3gpp_registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
- mm_iface_modem_3gpp_reload_current_operator (MM_IFACE_MODEM_3GPP (self), NULL, NULL);
+ mm_iface_modem_3gpp_reload_current_registration_info (MM_IFACE_MODEM_3GPP (self), NULL, NULL);
}
/* Done we are */
@@ -7925,6 +7986,8 @@ typedef enum {
DISABLING_STEP_DISCONNECT_BEARERS,
DISABLING_STEP_IFACE_SIMPLE,
DISABLING_STEP_IFACE_FIRMWARE,
+ DISABLING_STEP_IFACE_SIGNAL,
+ DISABLING_STEP_IFACE_OMA,
DISABLING_STEP_IFACE_TIME,
DISABLING_STEP_IFACE_MESSAGING,
DISABLING_STEP_IFACE_LOCATION,
@@ -8013,7 +8076,7 @@ disable_finish (MMBaseModem *self,
result, \
&error)) { \
if (FATAL_ERRORS) { \
- g_simple_async_result_take_error (G_SIMPLE_ASYNC_RESULT (ctx->result), error); \
+ g_simple_async_result_take_error (ctx->result, error); \
disabling_context_complete_and_free (ctx); \
return; \
} \
@@ -8035,7 +8098,9 @@ INTERFACE_DISABLE_READY_FN (iface_modem_3gpp_ussd, MM_IFACE_MODEM_3GPP_USSD, TRU
INTERFACE_DISABLE_READY_FN (iface_modem_cdma, MM_IFACE_MODEM_CDMA, TRUE)
INTERFACE_DISABLE_READY_FN (iface_modem_location, MM_IFACE_MODEM_LOCATION, FALSE)
INTERFACE_DISABLE_READY_FN (iface_modem_messaging, MM_IFACE_MODEM_MESSAGING, FALSE)
+INTERFACE_DISABLE_READY_FN (iface_modem_signal, MM_IFACE_MODEM_SIGNAL, FALSE)
INTERFACE_DISABLE_READY_FN (iface_modem_time, MM_IFACE_MODEM_TIME, FALSE)
+INTERFACE_DISABLE_READY_FN (iface_modem_oma, MM_IFACE_MODEM_OMA, FALSE)
static void
bearer_list_disconnect_all_bearers_ready (MMBearerList *list,
@@ -8045,7 +8110,7 @@ bearer_list_disconnect_all_bearers_ready (MMBearerList *list,
GError *error = NULL;
if (!mm_bearer_list_disconnect_all_bearers_finish (list, res, &error)) {
- g_simple_async_result_take_error (G_SIMPLE_ASYNC_RESULT (ctx->result), error);
+ g_simple_async_result_take_error (ctx->result, error);
disabling_context_complete_and_free (ctx);
return;
}
@@ -8064,7 +8129,7 @@ disabling_wait_for_final_state_ready (MMIfaceModem *self,
ctx->previous_state = mm_iface_modem_wait_for_final_state_finish (self, res, &error);
if (error) {
- g_simple_async_result_take_error (G_SIMPLE_ASYNC_RESULT (ctx->result), error);
+ g_simple_async_result_take_error (ctx->result, error);
disabling_context_complete_and_free (ctx);
return;
}
@@ -8133,6 +8198,30 @@ disabling_step (DisablingContext *ctx)
/* Fall down to next step */
ctx->step++;
+ case DISABLING_STEP_IFACE_SIGNAL:
+ if (ctx->self->priv->modem_signal_dbus_skeleton) {
+ mm_dbg ("Modem has extended signal reporting capabilities, disabling the Signal interface...");
+ /* Disabling the Modem Signal interface */
+ mm_iface_modem_signal_disable (MM_IFACE_MODEM_SIGNAL (ctx->self),
+ (GAsyncReadyCallback)iface_modem_signal_disable_ready,
+ ctx);
+ return;
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
+ case DISABLING_STEP_IFACE_OMA:
+ if (ctx->self->priv->modem_oma_dbus_skeleton) {
+ mm_dbg ("Modem has OMA capabilities, disabling the OMA interface...");
+ /* Disabling the Modem Oma interface */
+ mm_iface_modem_oma_disable (MM_IFACE_MODEM_OMA (ctx->self),
+ (GAsyncReadyCallback)iface_modem_oma_disable_ready,
+ ctx);
+ return;
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
case DISABLING_STEP_IFACE_TIME:
if (ctx->self->priv->modem_time_dbus_skeleton) {
mm_dbg ("Modem has time capabilities, disabling the Time interface...");
@@ -8225,7 +8314,7 @@ disabling_step (DisablingContext *ctx)
case DISABLING_STEP_LAST:
ctx->disabled = TRUE;
/* All disabled without errors! */
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (ctx->result), TRUE);
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
disabling_context_complete_and_free (ctx);
return;
}
@@ -8264,6 +8353,8 @@ typedef enum {
ENABLING_STEP_IFACE_LOCATION,
ENABLING_STEP_IFACE_MESSAGING,
ENABLING_STEP_IFACE_TIME,
+ ENABLING_STEP_IFACE_SIGNAL,
+ ENABLING_STEP_IFACE_OMA,
ENABLING_STEP_IFACE_FIRMWARE,
ENABLING_STEP_IFACE_SIMPLE,
ENABLING_STEP_LAST,
@@ -8340,7 +8431,7 @@ enable_finish (MMBaseModem *self,
result, \
&error)) { \
if (FATAL_ERRORS) { \
- g_simple_async_result_take_error (G_SIMPLE_ASYNC_RESULT (ctx->result), error); \
+ g_simple_async_result_take_error (ctx->result, error); \
enabling_context_complete_and_free (ctx); \
return; \
} \
@@ -8361,7 +8452,9 @@ INTERFACE_ENABLE_READY_FN (iface_modem_3gpp_ussd, MM_IFACE_MODEM_3GPP_USSD, TRUE
INTERFACE_ENABLE_READY_FN (iface_modem_cdma, MM_IFACE_MODEM_CDMA, TRUE)
INTERFACE_ENABLE_READY_FN (iface_modem_location, MM_IFACE_MODEM_LOCATION, FALSE)
INTERFACE_ENABLE_READY_FN (iface_modem_messaging, MM_IFACE_MODEM_MESSAGING, FALSE)
+INTERFACE_ENABLE_READY_FN (iface_modem_signal, MM_IFACE_MODEM_SIGNAL, FALSE)
INTERFACE_ENABLE_READY_FN (iface_modem_time, MM_IFACE_MODEM_TIME, FALSE)
+INTERFACE_ENABLE_READY_FN (iface_modem_oma, MM_IFACE_MODEM_OMA, FALSE)
static void
enabling_started_ready (MMBroadbandModem *self,
@@ -8371,7 +8464,7 @@ enabling_started_ready (MMBroadbandModem *self,
GError *error = NULL;
if (!MM_BROADBAND_MODEM_GET_CLASS (self)->enabling_started_finish (self, result, &error)) {
- g_simple_async_result_take_error (G_SIMPLE_ASYNC_RESULT (ctx->result), error);
+ g_simple_async_result_take_error (ctx->result, error);
enabling_context_complete_and_free (ctx);
return;
}
@@ -8390,7 +8483,7 @@ enabling_wait_for_final_state_ready (MMIfaceModem *self,
ctx->previous_state = mm_iface_modem_wait_for_final_state_finish (self, res, &error);
if (error) {
- g_simple_async_result_take_error (G_SIMPLE_ASYNC_RESULT (ctx->result), error);
+ g_simple_async_result_take_error (ctx->result, error);
enabling_context_complete_and_free (ctx);
return;
}
@@ -8531,6 +8624,32 @@ enabling_step (EnablingContext *ctx)
/* Fall down to next step */
ctx->step++;
+ case ENABLING_STEP_IFACE_SIGNAL:
+ if (ctx->self->priv->modem_signal_dbus_skeleton) {
+ mm_dbg ("Modem has extended signal reporting capabilities, enabling the Signal interface...");
+ /* Enabling the Modem Signal interface */
+ mm_iface_modem_signal_enable (MM_IFACE_MODEM_SIGNAL (ctx->self),
+ ctx->cancellable,
+ (GAsyncReadyCallback)iface_modem_signal_enable_ready,
+ ctx);
+ return;
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
+ case ENABLING_STEP_IFACE_OMA:
+ if (ctx->self->priv->modem_oma_dbus_skeleton) {
+ mm_dbg ("Modem has OMA capabilities, enabling the OMA interface...");
+ /* Enabling the Modem Oma interface */
+ mm_iface_modem_oma_enable (MM_IFACE_MODEM_OMA (ctx->self),
+ ctx->cancellable,
+ (GAsyncReadyCallback)iface_modem_oma_enable_ready,
+ ctx);
+ return;
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
case ENABLING_STEP_IFACE_FIRMWARE:
/* Fall down to next step */
ctx->step++;
@@ -8542,7 +8661,7 @@ enabling_step (EnablingContext *ctx)
case ENABLING_STEP_LAST:
ctx->enabled = TRUE;
/* All enabled without errors! */
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (ctx->result), TRUE);
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
enabling_context_complete_and_free (ctx);
return;
}
@@ -8606,7 +8725,7 @@ enable (MMBaseModem *self,
case MM_MODEM_STATE_ENABLING:
g_simple_async_result_set_error (result,
MM_CORE_ERROR,
- MM_CORE_ERROR_WRONG_STATE,
+ MM_CORE_ERROR_IN_PROGRESS,
"Cannot enable modem: "
"already being enabled");
break;
@@ -8641,6 +8760,8 @@ typedef enum {
INITIALIZE_STEP_IFACE_LOCATION,
INITIALIZE_STEP_IFACE_MESSAGING,
INITIALIZE_STEP_IFACE_TIME,
+ INITIALIZE_STEP_IFACE_SIGNAL,
+ INITIALIZE_STEP_IFACE_OMA,
INITIALIZE_STEP_IFACE_FIRMWARE,
INITIALIZE_STEP_IFACE_SIMPLE,
INITIALIZE_STEP_LAST,
@@ -8836,6 +8957,8 @@ INTERFACE_INIT_READY_FN (iface_modem_cdma, MM_IFACE_MODEM_CDMA, TRUE)
INTERFACE_INIT_READY_FN (iface_modem_location, MM_IFACE_MODEM_LOCATION, FALSE)
INTERFACE_INIT_READY_FN (iface_modem_messaging, MM_IFACE_MODEM_MESSAGING, FALSE)
INTERFACE_INIT_READY_FN (iface_modem_time, MM_IFACE_MODEM_TIME, FALSE)
+INTERFACE_INIT_READY_FN (iface_modem_signal, MM_IFACE_MODEM_SIGNAL, FALSE)
+INTERFACE_INIT_READY_FN (iface_modem_oma, MM_IFACE_MODEM_OMA, FALSE)
INTERFACE_INIT_READY_FN (iface_modem_firmware, MM_IFACE_MODEM_FIRMWARE, FALSE)
static void
@@ -8948,6 +9071,22 @@ initialize_step (InitializeContext *ctx)
ctx);
return;
+ case INITIALIZE_STEP_IFACE_SIGNAL:
+ /* Initialize the Signal interface */
+ mm_iface_modem_signal_initialize (MM_IFACE_MODEM_SIGNAL (ctx->self),
+ ctx->cancellable,
+ (GAsyncReadyCallback)iface_modem_signal_initialize_ready,
+ ctx);
+ return;
+
+ case INITIALIZE_STEP_IFACE_OMA:
+ /* Initialize the Oma interface */
+ mm_iface_modem_oma_initialize (MM_IFACE_MODEM_OMA (ctx->self),
+ ctx->cancellable,
+ (GAsyncReadyCallback)iface_modem_oma_initialize_ready,
+ ctx);
+ return;
+
case INITIALIZE_STEP_IFACE_FIRMWARE:
/* Initialize the Firmware interface */
mm_iface_modem_firmware_initialize (MM_IFACE_MODEM_FIRMWARE (ctx->self),
@@ -9011,7 +9150,7 @@ initialize_step (InitializeContext *ctx)
MM_MODEM_STATE_DISABLED,
MM_MODEM_STATE_CHANGE_REASON_UNKNOWN);
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (ctx->result), TRUE);
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
initialize_context_complete_and_free (ctx);
return;
}
@@ -9062,7 +9201,7 @@ initialize (MMBaseModem *self,
case MM_MODEM_STATE_INITIALIZING:
g_simple_async_result_set_error (result,
MM_CORE_ERROR,
- MM_CORE_ERROR_WRONG_STATE,
+ MM_CORE_ERROR_IN_PROGRESS,
"Cannot initialize modem: "
"already being initialized");
break;
@@ -9189,6 +9328,14 @@ set_property (GObject *object,
g_clear_object (&self->priv->modem_time_dbus_skeleton);
self->priv->modem_time_dbus_skeleton = g_value_dup_object (value);
break;
+ case PROP_MODEM_SIGNAL_DBUS_SKELETON:
+ g_clear_object (&self->priv->modem_signal_dbus_skeleton);
+ self->priv->modem_signal_dbus_skeleton = g_value_dup_object (value);
+ break;
+ case PROP_MODEM_OMA_DBUS_SKELETON:
+ g_clear_object (&self->priv->modem_oma_dbus_skeleton);
+ self->priv->modem_oma_dbus_skeleton = g_value_dup_object (value);
+ break;
case PROP_MODEM_FIRMWARE_DBUS_SKELETON:
g_clear_object (&self->priv->modem_firmware_dbus_skeleton);
self->priv->modem_firmware_dbus_skeleton = g_value_dup_object (value);
@@ -9284,6 +9431,12 @@ get_property (GObject *object,
case PROP_MODEM_TIME_DBUS_SKELETON:
g_value_set_object (value, self->priv->modem_time_dbus_skeleton);
break;
+ case PROP_MODEM_SIGNAL_DBUS_SKELETON:
+ g_value_set_object (value, self->priv->modem_signal_dbus_skeleton);
+ break;
+ case PROP_MODEM_OMA_DBUS_SKELETON:
+ g_value_set_object (value, self->priv->modem_oma_dbus_skeleton);
+ break;
case PROP_MODEM_FIRMWARE_DBUS_SKELETON:
g_value_set_object (value, self->priv->modem_firmware_dbus_skeleton);
break;
@@ -9517,6 +9670,8 @@ iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
iface->load_operator_code_finish = modem_3gpp_load_operator_code_finish;
iface->load_operator_name = modem_3gpp_load_operator_name;
iface->load_operator_name_finish = modem_3gpp_load_operator_name_finish;
+ iface->load_subscription_state = modem_3gpp_load_subscription_state;
+ iface->load_subscription_state_finish = modem_3gpp_load_subscription_state_finish;
iface->run_registration_checks = modem_3gpp_run_registration_checks;
iface->run_registration_checks_finish = modem_3gpp_run_registration_checks_finish;
iface->register_in_network = modem_3gpp_register_in_network;
@@ -9623,6 +9778,16 @@ iface_modem_time_init (MMIfaceModemTime *iface)
}
static void
+iface_modem_signal_init (MMIfaceModemSignal *iface)
+{
+}
+
+static void
+iface_modem_oma_init (MMIfaceModemOma *iface)
+{
+}
+
+static void
iface_modem_firmware_init (MMIfaceModemFirmware *iface)
{
}
@@ -9691,6 +9856,14 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass)
MM_IFACE_MODEM_TIME_DBUS_SKELETON);
g_object_class_override_property (object_class,
+ PROP_MODEM_SIGNAL_DBUS_SKELETON,
+ MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON);
+
+ g_object_class_override_property (object_class,
+ PROP_MODEM_OMA_DBUS_SKELETON,
+ MM_IFACE_MODEM_OMA_DBUS_SKELETON);
+
+ g_object_class_override_property (object_class,
PROP_MODEM_FIRMWARE_DBUS_SKELETON,
MM_IFACE_MODEM_FIRMWARE_DBUS_SKELETON);
diff --git a/src/mm-error-helpers.c b/src/mm-error-helpers.c
index 6e63b1b..e922cbb 100644
--- a/src/mm-error-helpers.c
+++ b/src/mm-error-helpers.c
@@ -50,7 +50,7 @@ mm_connection_error_for_code (MMConnectionError code)
break;
default:
- g_warning ("Invalid connection error code: %u", code);
+ g_debug ("Invalid connection error code: %u", code);
/* uhm... make something up (yes, ok, lie!). */
code = MM_CONNECTION_ERROR_NO_CARRIER;
msg = "No carrier";
@@ -127,7 +127,7 @@ mm_mobile_equipment_error_for_code (MMMobileEquipmentError code)
}
/* Not found? Then, default */
- g_warning ("Invalid mobile equipment error code: %u", (guint)code);
+ g_debug ("Invalid mobile equipment error code: %u", (guint)code);
return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN,
"Unknown error");
@@ -163,8 +163,8 @@ mm_mobile_equipment_error_for_string (const gchar *str)
/* Not found? Then, default */
if (!msg) {
- g_warning ("Invalid mobile equipment error string: '%s' (%s)",
- str, buf);
+ g_debug ("Invalid mobile equipment error string: '%s' (%s)",
+ str, buf);
code = MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN;
msg = "Unknown error";
}
@@ -215,7 +215,7 @@ mm_message_error_for_code (MMMessageError code)
}
/* Not found? Then, default */
- g_warning ("Invalid message error code: %u", (guint)code);
+ g_debug ("Invalid message error code: %u", (guint)code);
return g_error_new (MM_MESSAGE_ERROR,
MM_MESSAGE_ERROR_UNKNOWN,
"Unknown error");
@@ -251,8 +251,8 @@ mm_message_error_for_string (const gchar *str)
/* Not found? Then, default */
if (!msg) {
- g_warning ("Invalid message error string: '%s' (%s)",
- str, buf);
+ g_debug ("Invalid message error string: '%s' (%s)",
+ str, buf);
code = MM_MESSAGE_ERROR_UNKNOWN;
msg = "Unknown error";
}
diff --git a/src/mm-iface-modem-3gpp.c b/src/mm-iface-modem-3gpp.c
index 53399d5..d6c149a 100644
--- a/src/mm-iface-modem-3gpp.c
+++ b/src/mm-iface-modem-3gpp.c
@@ -64,6 +64,10 @@ mm_iface_modem_3gpp_bind_simple_status (MMIfaceModem3gpp *self,
status, MM_SIMPLE_PROPERTY_3GPP_OPERATOR_NAME,
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+ g_object_bind_property (skeleton, "subscription-state",
+ status, MM_SIMPLE_PROPERTY_3GPP_SUBSCRIPTION_STATE,
+ G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+
g_object_unref (skeleton);
}
@@ -75,7 +79,7 @@ typedef struct {
MMModem3gppRegistrationState eps;
gboolean manual_registration;
GCancellable *pending_registration_cancellable;
- gboolean reloading_operator;
+ gboolean reloading_registration_info;
} RegistrationStateContext;
static void
@@ -760,33 +764,34 @@ typedef struct {
GSimpleAsyncResult *result;
gboolean operator_code_loaded;
gboolean operator_name_loaded;
-} ReloadCurrentOperatorContext;
+ gboolean subscription_state_loaded;
+} ReloadCurrentRegistrationInfoContext;
static void
-reload_current_operator_context_complete_and_free (ReloadCurrentOperatorContext *ctx)
+reload_current_registration_info_context_complete_and_free (ReloadCurrentRegistrationInfoContext *ctx)
{
g_simple_async_result_complete_in_idle (ctx->result);
g_object_unref (ctx->result);
if (ctx->skeleton)
g_object_unref (ctx->skeleton);
g_object_unref (ctx->self);
- g_slice_free (ReloadCurrentOperatorContext, ctx);
+ g_slice_free (ReloadCurrentRegistrationInfoContext, ctx);
}
gboolean
-mm_iface_modem_3gpp_reload_current_operator_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
+mm_iface_modem_3gpp_reload_current_registration_info_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
{
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
-static void reload_current_operator_context_step (ReloadCurrentOperatorContext *ctx);
+static void reload_current_registration_info_context_step (ReloadCurrentRegistrationInfoContext *ctx);
static void
load_operator_name_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- ReloadCurrentOperatorContext *ctx)
+ ReloadCurrentRegistrationInfoContext *ctx)
{
GError *error = NULL;
gchar *str;
@@ -802,7 +807,7 @@ load_operator_name_ready (MMIfaceModem3gpp *self,
g_free (str);
ctx->operator_name_loaded = TRUE;
- reload_current_operator_context_step (ctx);
+ reload_current_registration_info_context_step (ctx);
}
static gboolean
@@ -836,7 +841,7 @@ parse_mcc_mnc (const gchar *mccmnc,
static void
load_operator_code_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- ReloadCurrentOperatorContext *ctx)
+ ReloadCurrentRegistrationInfoContext *ctx)
{
GError *error = NULL;
gchar *str;
@@ -861,11 +866,33 @@ load_operator_code_ready (MMIfaceModem3gpp *self,
g_free (str);
ctx->operator_code_loaded = TRUE;
- reload_current_operator_context_step (ctx);
+ reload_current_registration_info_context_step (ctx);
+}
+
+static void
+load_subscription_state_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ ReloadCurrentRegistrationInfoContext *ctx)
+{
+ GError *error = NULL;
+ MMModem3gppSubscriptionState subscription_state = MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN;
+
+ subscription_state = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_subscription_state_finish (self, res, &error);
+ if (error) {
+ mm_warn ("Couldn't load Subscription State: '%s'", error->message);
+ g_error_free (error);
+ }
+
+ if (ctx->skeleton)
+ mm_gdbus_modem3gpp_set_subscription_state (ctx->skeleton, subscription_state);
+
+ ctx->subscription_state_loaded = TRUE;
+ reload_current_registration_info_context_step (ctx);
}
+
static void
-reload_current_operator_context_step (ReloadCurrentOperatorContext *ctx)
+reload_current_registration_info_context_step (ReloadCurrentRegistrationInfoContext *ctx)
{
if (!ctx->operator_code_loaded) {
/* Launch operator code update */
@@ -885,24 +912,33 @@ reload_current_operator_context_step (ReloadCurrentOperatorContext *ctx)
return;
}
- /* If both loaded, all done */
+ if (!ctx->subscription_state_loaded) {
+ /* Launch subscription state update */
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->load_subscription_state (
+ ctx->self,
+ (GAsyncReadyCallback)load_subscription_state_ready,
+ ctx);
+ return;
+ }
+
+ /* If all are loaded, all done */
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- reload_current_operator_context_complete_and_free (ctx);
+ reload_current_registration_info_context_complete_and_free (ctx);
}
void
-mm_iface_modem_3gpp_reload_current_operator (MMIfaceModem3gpp *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mm_iface_modem_3gpp_reload_current_registration_info (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- ReloadCurrentOperatorContext *ctx;
+ ReloadCurrentRegistrationInfoContext *ctx;
- ctx = g_slice_new0 (ReloadCurrentOperatorContext);
+ ctx = g_slice_new0 (ReloadCurrentRegistrationInfoContext);
ctx->self = g_object_ref (self);
ctx->result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
- mm_iface_modem_3gpp_reload_current_operator);
+ mm_iface_modem_3gpp_reload_current_registration_info);
g_object_get (self,
MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &ctx->skeleton,
@@ -912,7 +948,7 @@ mm_iface_modem_3gpp_reload_current_operator (MMIfaceModem3gpp *self,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't get interface skeleton");
- reload_current_operator_context_complete_and_free (ctx);
+ reload_current_registration_info_context_complete_and_free (ctx);
return;
}
@@ -929,7 +965,12 @@ mm_iface_modem_3gpp_reload_current_operator (MMIfaceModem3gpp *self,
if (ctx->operator_name_loaded)
mm_gdbus_modem3gpp_set_operator_name (ctx->skeleton, NULL);
- reload_current_operator_context_step (ctx);
+ ctx->subscription_state_loaded = !(MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_subscription_state &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_subscription_state_finish);
+ if (ctx->subscription_state_loaded)
+ mm_gdbus_modem3gpp_set_subscription_state (ctx->skeleton, MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN);
+
+ reload_current_registration_info_context_step (ctx);
}
void
@@ -949,6 +990,19 @@ mm_iface_modem_3gpp_clear_current_operator (MMIfaceModem3gpp *self)
mm_iface_modem_location_3gpp_update_mcc_mnc (MM_IFACE_MODEM_LOCATION (self), 0, 0);
}
+static void
+clear_subscription_state (MMIfaceModem3gpp *self)
+{
+ MmGdbusModem3gpp *skeleton = NULL;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (!skeleton)
+ return;
+ mm_gdbus_modem3gpp_set_subscription_state (skeleton, MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN);
+}
+
/*****************************************************************************/
void
@@ -969,7 +1023,7 @@ mm_iface_modem_3gpp_update_access_technologies (MMIfaceModem3gpp *self,
* but only if something valid to report */
if (state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING ||
- ctx->reloading_operator) {
+ ctx->reloading_registration_info) {
if (access_tech != MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN)
mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self),
access_tech,
@@ -1009,9 +1063,9 @@ mm_iface_modem_3gpp_update_location (MMIfaceModem3gpp *self,
/*****************************************************************************/
static void
-update_registration_reload_current_operator_ready (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- gpointer user_data)
+update_registration_reload_current_registration_info_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ gpointer user_data)
{
MMModem3gppRegistrationState new_state;
RegistrationStateContext *ctx;
@@ -1034,7 +1088,7 @@ update_registration_reload_current_operator_ready (MMIfaceModem3gpp *self,
MM_MODEM_STATE_CHANGE_REASON_UNKNOWN);
ctx = get_registration_state_context (self);
- ctx->reloading_operator = FALSE;
+ ctx->reloading_registration_info = FALSE;
}
static void
@@ -1045,6 +1099,14 @@ update_non_registered_state (MMIfaceModem3gpp *self,
/* Not registered neither in home nor roaming network */
mm_iface_modem_3gpp_clear_current_operator (self);
+ /* The subscription state can be computed in two ways: a) via PCO which is
+ * sent by the carrier during registration or b) by looking at the
+ * registration reject error code. If b), we want to make sure we
+ * preserve the subscription state */
+ if (old_state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
+ old_state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
+ clear_subscription_state (self);
+
/* The property in the interface is bound to the property
* in the skeleton, so just updating here is enough */
g_object_set (self,
@@ -1081,20 +1143,20 @@ update_registration_state (MMIfaceModem3gpp *self,
if (new_state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
new_state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) {
- /* If already reloading operator, skip it */
- if (ctx->reloading_operator)
+ /* If already reloading registration info, skip it */
+ if (ctx->reloading_registration_info)
return;
mm_info ("Modem %s: 3GPP Registration state changed (%s -> registering)",
g_dbus_object_get_object_path (G_DBUS_OBJECT (self)),
mm_modem_3gpp_registration_state_get_string (old_state));
- /* Reload current operator. ONLY update the state to REGISTERED after
- * having loaded operator code/name */
- ctx->reloading_operator = TRUE;
- mm_iface_modem_3gpp_reload_current_operator (
+ /* Reload current registration info. ONLY update the state to REGISTERED
+ * after having loaded operator code/name/subscription state */
+ ctx->reloading_registration_info = TRUE;
+ mm_iface_modem_3gpp_reload_current_registration_info (
self,
- (GAsyncReadyCallback)update_registration_reload_current_operator_ready,
+ (GAsyncReadyCallback)update_registration_reload_current_registration_info_ready,
GUINT_TO_POINTER (new_state));
return;
}
@@ -1164,6 +1226,22 @@ mm_iface_modem_3gpp_update_eps_registration_state (MMIfaceModem3gpp *self,
update_registration_state (self, get_consolidated_reg_state (ctx), TRUE);
}
+void
+mm_iface_modem_3gpp_update_subscription_state (MMIfaceModem3gpp *self,
+ MMModem3gppSubscriptionState state)
+{
+ MmGdbusModem3gpp *skeleton = NULL;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (skeleton) {
+ mm_dbg ("Setting subscription state to: %s", mm_modem_3gpp_subscription_state_get_string (state));
+ mm_gdbus_modem3gpp_set_subscription_state (skeleton, state);
+ g_object_unref (skeleton);
+ }
+}
+
/*****************************************************************************/
typedef struct {
@@ -1954,6 +2032,7 @@ mm_iface_modem_3gpp_initialize (MMIfaceModem3gpp *self,
mm_gdbus_modem3gpp_set_operator_code (skeleton, NULL);
mm_gdbus_modem3gpp_set_operator_name (skeleton, NULL);
mm_gdbus_modem3gpp_set_enabled_facility_locks (skeleton, MM_MODEM_3GPP_FACILITY_NONE);
+ mm_gdbus_modem3gpp_set_subscription_state (skeleton, MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN);
/* Bind our RegistrationState property */
g_object_bind_property (self, MM_IFACE_MODEM_3GPP_REGISTRATION_STATE,
diff --git a/src/mm-iface-modem-3gpp.h b/src/mm-iface-modem-3gpp.h
index f4014c4..8497d20 100644
--- a/src/mm-iface-modem-3gpp.h
+++ b/src/mm-iface-modem-3gpp.h
@@ -176,6 +176,14 @@ struct _MMIfaceModem3gpp {
GAsyncResult *res,
GError **error);
+ /* Loading of the subscription state property */
+ void (*load_subscription_state) (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ MMModem3gppSubscriptionState (*load_subscription_state_finish) (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
+
/* Scan current networks, expect a GList of MMModem3gppNetworkInfo */
void (* scan_networks) (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
@@ -216,7 +224,7 @@ gboolean mm_iface_modem_3gpp_disable_finish (MMIfaceModem3gpp *self,
/* Shutdown Modem 3GPP interface */
void mm_iface_modem_3gpp_shutdown (MMIfaceModem3gpp *self);
-/* Objects implementing this interface can report new registration states,
+/* Objects implementing this interface can report new registration info,
* access technologies and location.
* This may happen when handling unsolicited registration messages, or when
* the interface asks to run registration state checks. */
@@ -226,6 +234,8 @@ void mm_iface_modem_3gpp_update_ps_registration_state (MMIfaceModem3gpp *self,
MMModem3gppRegistrationState state);
void mm_iface_modem_3gpp_update_eps_registration_state (MMIfaceModem3gpp *self,
MMModem3gppRegistrationState state);
+void mm_iface_modem_3gpp_update_subscription_state (MMIfaceModem3gpp *self,
+ MMModem3gppSubscriptionState state);
void mm_iface_modem_3gpp_update_access_technologies (MMIfaceModem3gpp *self,
MMModemAccessTechnology access_tech);
void mm_iface_modem_3gpp_update_location (MMIfaceModem3gpp *self,
@@ -240,14 +250,14 @@ gboolean mm_iface_modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *s
GAsyncResult *res,
GError **error);
-/* Request to reload current operator */
-void mm_iface_modem_3gpp_reload_current_operator (MMIfaceModem3gpp *self,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean mm_iface_modem_3gpp_reload_current_operator_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error);
-void mm_iface_modem_3gpp_clear_current_operator (MMIfaceModem3gpp *self);
+/* Request to reload current registration information */
+void mm_iface_modem_3gpp_reload_current_registration_info (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_3gpp_reload_current_registration_info_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_iface_modem_3gpp_clear_current_operator (MMIfaceModem3gpp *self);
/* Allow registering in the network */
gboolean mm_iface_modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self,
diff --git a/src/mm-iface-modem-cdma.c b/src/mm-iface-modem-cdma.c
index c53c6b5..853f1db 100644
--- a/src/mm-iface-modem-cdma.c
+++ b/src/mm-iface-modem-cdma.c
@@ -227,7 +227,7 @@ typedef struct {
MmGdbusModemCdma *skeleton;
GDBusMethodInvocation *invocation;
MMIfaceModemCdma *self;
- GVariant *properties;
+ GVariant *dictionary;
} HandleActivateManualContext;
static void
@@ -236,7 +236,7 @@ handle_activate_manual_context_free (HandleActivateManualContext *ctx)
g_object_unref (ctx->skeleton);
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
- g_variant_unref (ctx->properties);
+ g_variant_unref (ctx->dictionary);
g_free (ctx);
}
@@ -260,6 +260,7 @@ handle_activate_manual_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleActivateManualContext *ctx)
{
+ MMCdmaManualActivationProperties *properties;
MMModemState modem_state;
GError *error = NULL;
@@ -277,7 +278,7 @@ handle_activate_manual_auth_ready (MMBaseModem *self,
return;
}
- /* If activating OTA is not implemented, report an error */
+ /* If manual activation is not implemented, report an error */
if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->activate_manual ||
!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->activate_manual_finish) {
g_dbus_method_invocation_return_error (ctx->invocation,
@@ -289,6 +290,14 @@ handle_activate_manual_auth_ready (MMBaseModem *self,
return;
}
+ /* Parse input properties */
+ properties = mm_cdma_manual_activation_properties_new_from_dictionary (ctx->dictionary, &error);
+ if (!properties) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_activate_manual_context_free (ctx);
+ return;
+ }
+
modem_state = MM_MODEM_STATE_UNKNOWN;
g_object_get (self,
MM_IFACE_MODEM_STATE, &modem_state,
@@ -308,17 +317,17 @@ handle_activate_manual_auth_ready (MMBaseModem *self,
MM_CORE_ERROR_WRONG_STATE,
"Cannot perform manual activation: "
"device not fully initialized yet");
- handle_activate_manual_context_free (ctx);
- return;
+ break;
case MM_MODEM_STATE_ENABLED:
case MM_MODEM_STATE_SEARCHING:
case MM_MODEM_STATE_REGISTERED:
MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->activate_manual (
MM_IFACE_MODEM_CDMA (self),
- ctx->properties,
+ properties,
(GAsyncReadyCallback)handle_activate_manual_ready,
ctx);
+ g_object_unref (properties);
return;
case MM_MODEM_STATE_DISABLING:
@@ -349,13 +358,14 @@ handle_activate_manual_auth_ready (MMBaseModem *self,
break;
}
+ g_object_unref (properties);
handle_activate_manual_context_free (ctx);
}
static gboolean
handle_activate_manual (MmGdbusModemCdma *skeleton,
GDBusMethodInvocation *invocation,
- GVariant *properties,
+ GVariant *dictionary,
MMIfaceModemCdma *self)
{
HandleActivateManualContext *ctx;
@@ -364,7 +374,7 @@ handle_activate_manual (MmGdbusModemCdma *skeleton,
ctx->skeleton = g_object_ref (skeleton);
ctx->invocation = g_object_ref (invocation);
ctx->self = g_object_ref (self);
- ctx->properties = g_variant_ref (properties);
+ ctx->dictionary = g_variant_ref (dictionary);
mm_base_modem_authorize (MM_BASE_MODEM (self),
invocation,
diff --git a/src/mm-iface-modem-cdma.h b/src/mm-iface-modem-cdma.h
index e6a6cdd..a7efe91 100644
--- a/src/mm-iface-modem-cdma.h
+++ b/src/mm-iface-modem-cdma.h
@@ -120,7 +120,7 @@ struct _MMIfaceModemCdma {
/* Manual activation */
void (* activate_manual) (MMIfaceModemCdma *self,
- GVariant *properties,
+ MMCdmaManualActivationProperties *properties,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean (* activate_manual_finish) (MMIfaceModemCdma *self,
diff --git a/src/mm-iface-modem-messaging.c b/src/mm-iface-modem-messaging.c
index 143d7ef..1101862 100644
--- a/src/mm-iface-modem-messaging.c
+++ b/src/mm-iface-modem-messaging.c
@@ -490,6 +490,17 @@ mm_iface_modem_messaging_is_storage_supported_for_receiving (MMIfaceModemMessagi
/*****************************************************************************/
static void
+update_message_list (MmGdbusModemMessaging *skeleton,
+ MMSmsList *list)
+{
+ gchar **paths;
+
+ paths = mm_sms_list_get_paths (list);
+ mm_gdbus_modem_messaging_set_messages (skeleton, (const gchar *const *)paths);
+ g_strfreev (paths);
+}
+
+static void
sms_added (MMSmsList *list,
const gchar *sms_path,
gboolean received,
@@ -498,6 +509,7 @@ sms_added (MMSmsList *list,
mm_dbg ("Added %s SMS at '%s'",
received ? "received" : "local",
sms_path);
+ update_message_list (skeleton, list);
mm_gdbus_modem_messaging_emit_added (skeleton, sms_path, received);
}
@@ -507,6 +519,7 @@ sms_deleted (MMSmsList *list,
MmGdbusModemMessaging *skeleton)
{
mm_dbg ("Deleted SMS at '%s'", sms_path);
+ update_message_list (skeleton, list);
mm_gdbus_modem_messaging_emit_deleted (skeleton, sms_path);
}
@@ -1079,6 +1092,21 @@ initialization_context_complete_and_free_if_cancelled (InitializationContext *ct
}
static void
+skip_unknown_storages (GArray *mem)
+{
+ guint i = mem->len;
+
+ if (!mem)
+ return;
+
+ /* Remove UNKNOWN from the list of supported storages */
+ while (i-- > 0) {
+ if (g_array_index (mem, MMSmsStorage, i) == MM_SMS_STORAGE_UNKNOWN)
+ g_array_remove_index (mem, i);
+ }
+}
+
+static void
load_supported_storages_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
InitializationContext *ctx)
@@ -1103,6 +1131,11 @@ load_supported_storages_ready (MMIfaceModemMessaging *self,
GArray *supported_storages;
guint i;
+ /* Never add unknown storages */
+ skip_unknown_storages (storage_ctx->supported_mem1);
+ skip_unknown_storages (storage_ctx->supported_mem2);
+ skip_unknown_storages (storage_ctx->supported_mem3);
+
mem1 = mm_common_build_sms_storages_string ((MMSmsStorage *)storage_ctx->supported_mem1->data,
storage_ctx->supported_mem1->len);
mem2 = mm_common_build_sms_storages_string ((MMSmsStorage *)storage_ctx->supported_mem2->data,
diff --git a/src/mm-iface-modem-oma.c b/src/mm-iface-modem-oma.c
new file mode 100644
index 0000000..b8c4841
--- /dev/null
+++ b/src/mm-iface-modem-oma.c
@@ -0,0 +1,1265 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2013 Google, Inc.
+ */
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-oma.h"
+#include "mm-log.h"
+
+#define SUPPORT_CHECKED_TAG "oma-support-checked-tag"
+#define SUPPORTED_TAG "oma-supported-tag"
+
+static GQuark support_checked_quark;
+static GQuark supported_quark;
+
+/*****************************************************************************/
+
+void
+mm_iface_modem_oma_bind_simple_status (MMIfaceModemOma *self,
+ MMSimpleStatus *status)
+{
+}
+
+/*****************************************************************************/
+/* Manage the list of pending network-initiated sessions */
+
+static void
+add_or_remove_pending_network_initiated_session (MMIfaceModemOma *self,
+ gboolean add,
+ MMOmaSessionType session_type,
+ guint session_id)
+{
+ MmGdbusModemOma *skeleton;
+ GVariant *variant;
+ GArray *array;
+ guint i;
+
+ g_assert (session_type != MM_OMA_SESSION_TYPE_UNKNOWN);
+
+ g_object_get (self,
+ MM_IFACE_MODEM_OMA_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (!skeleton)
+ return;
+
+ variant = mm_gdbus_modem_oma_get_pending_network_initiated_sessions (skeleton);
+ array = mm_common_oma_pending_network_initiated_sessions_variant_to_garray (variant);
+
+ for (i = 0; i < array->len; i++) {
+ MMOmaPendingNetworkInitiatedSession *session;
+
+ session = &g_array_index (array, MMOmaPendingNetworkInitiatedSession, i);
+ if (session->session_id == session_id)
+ break;
+ }
+
+ /* If not in the array, and we want to add it, add it */
+ if (add && i == array->len) {
+ MMOmaPendingNetworkInitiatedSession session;
+
+ session.session_type = session_type;
+ session.session_id = session_id;
+ g_array_append_val (array, session);
+
+ mm_gdbus_modem_oma_set_pending_network_initiated_sessions (
+ skeleton,
+ mm_common_oma_pending_network_initiated_sessions_garray_to_variant (array));
+ }
+ /* If found in the array, and we want to remove it, remove it */
+ else if (!add && i < array->len) {
+ g_array_remove_index (array, i);
+ mm_gdbus_modem_oma_set_pending_network_initiated_sessions (
+ skeleton,
+ mm_common_oma_pending_network_initiated_sessions_garray_to_variant (array));
+ }
+
+ g_object_unref (skeleton);
+ g_array_unref (array);
+}
+
+void
+mm_iface_modem_oma_add_pending_network_initiated_session (MMIfaceModemOma *self,
+ MMOmaSessionType session_type,
+ guint session_id)
+{
+ add_or_remove_pending_network_initiated_session (self, TRUE, session_type, session_id);
+}
+
+/*****************************************************************************/
+/* New session state reported */
+
+void
+mm_iface_modem_oma_update_session_state (MMIfaceModemOma *self,
+ MMOmaSessionState new_session_state,
+ MMOmaSessionStateFailedReason session_state_failed_reason)
+{
+ MmGdbusModemOma *skeleton;
+ MMOmaSessionState old_session_state;
+
+ /* Make sure proper state vs failed reasons are given */
+ g_return_if_fail ((new_session_state != MM_OMA_SESSION_STATE_FAILED &&
+ session_state_failed_reason == MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN) ||
+ (new_session_state == MM_OMA_SESSION_STATE_FAILED));
+
+ g_object_get (self,
+ MM_IFACE_MODEM_OMA_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (!skeleton)
+ return;
+
+ old_session_state = mm_gdbus_modem_oma_get_session_state (skeleton);
+ if (old_session_state != new_session_state) {
+ mm_info ("Modem %s: OMA session state changed (%s -> %s)",
+ g_dbus_object_get_object_path (G_DBUS_OBJECT (self)),
+ mm_oma_session_state_get_string (old_session_state),
+ mm_oma_session_state_get_string (new_session_state));
+
+ /* Flush current change before signaling the state change,
+ * so that clients get the proper state already in the
+ * state-changed callback */
+ mm_gdbus_modem_oma_set_session_state (skeleton, new_session_state);
+ g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton));
+ mm_gdbus_modem_oma_emit_session_state_changed (
+ skeleton,
+ old_session_state,
+ new_session_state,
+ session_state_failed_reason);
+ }
+}
+
+/*****************************************************************************/
+/* Handle Setup() */
+
+typedef struct {
+ MmGdbusModemOma *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModemOma *self;
+ guint32 features;
+} HandleSetupContext;
+
+static void
+handle_setup_context_free (HandleSetupContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleSetupContext, ctx);
+}
+
+static void
+setup_ready (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ HandleSetupContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup_finish (self, res, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else {
+ /* Update current features in the interface */
+ mm_gdbus_modem_oma_set_features (ctx->skeleton, ctx->features);
+ mm_gdbus_modem_oma_complete_setup (ctx->skeleton, ctx->invocation);
+ }
+
+ handle_setup_context_free (ctx);
+}
+
+static void
+handle_setup_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleSetupContext *ctx)
+{
+ GError *error = NULL;
+ MMModemState modem_state;
+ gchar *str;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_setup_context_free (ctx);
+ return;
+ }
+
+ modem_state = MM_MODEM_STATE_UNKNOWN;
+ g_object_get (self,
+ MM_IFACE_MODEM_STATE, &modem_state,
+ NULL);
+ if (modem_state < MM_MODEM_STATE_ENABLED) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cannot setup OMA: "
+ "device not yet enabled");
+ handle_setup_context_free (ctx);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup ||
+ !MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot setup OMA: "
+ "operation not supported");
+ handle_setup_context_free (ctx);
+ return;
+ }
+
+ str = mm_oma_feature_build_string_from_mask (ctx->features);
+ mm_dbg ("Setting up OMA features: '%s'", str);
+ g_free (str);
+
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup (
+ ctx->self,
+ ctx->features,
+ (GAsyncReadyCallback)setup_ready,
+ ctx);
+}
+
+static gboolean
+handle_setup (MmGdbusModemOma *skeleton,
+ GDBusMethodInvocation *invocation,
+ guint32 features,
+ MMIfaceModemOma *self)
+{
+ HandleSetupContext *ctx;
+
+ ctx = g_slice_new0 (HandleSetupContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->features = features;
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_setup_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Handle StartClientInitiatedSession() */
+
+typedef struct {
+ MmGdbusModemOma *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModemOma *self;
+ MMOmaSessionType session_type;
+} HandleStartClientInitiatedSessionContext;
+
+static void
+handle_start_client_initiated_session_context_free (HandleStartClientInitiatedSessionContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleStartClientInitiatedSessionContext, ctx);
+}
+
+static void
+start_client_initiated_session_ready (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ HandleStartClientInitiatedSessionContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->start_client_initiated_session_finish (self, res, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else {
+ /* Update interface info */
+ mm_gdbus_modem_oma_set_session_type (ctx->skeleton, ctx->session_type);
+ mm_iface_modem_oma_update_session_state (self, MM_OMA_SESSION_STATE_STARTED, MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN);
+ mm_gdbus_modem_oma_complete_start_client_initiated_session (ctx->skeleton, ctx->invocation);
+ }
+
+ handle_start_client_initiated_session_context_free (ctx);
+}
+
+static void
+handle_start_client_initiated_session_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleStartClientInitiatedSessionContext *ctx)
+{
+ GError *error = NULL;
+ MMModemState modem_state;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_start_client_initiated_session_context_free (ctx);
+ return;
+ }
+
+ modem_state = MM_MODEM_STATE_UNKNOWN;
+ g_object_get (self,
+ MM_IFACE_MODEM_STATE, &modem_state,
+ NULL);
+ if (modem_state < MM_MODEM_STATE_ENABLED) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cannot start client-initiated OMA session: "
+ "device not yet enabled");
+ handle_start_client_initiated_session_context_free (ctx);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->start_client_initiated_session ||
+ !MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->start_client_initiated_session_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot start client-initiated OMA session: "
+ "operation not supported");
+ handle_start_client_initiated_session_context_free (ctx);
+ return;
+ }
+
+ if (ctx->session_type != MM_OMA_SESSION_TYPE_CLIENT_INITIATED_DEVICE_CONFIGURE &&
+ ctx->session_type != MM_OMA_SESSION_TYPE_CLIENT_INITIATED_PRL_UPDATE &&
+ ctx->session_type != MM_OMA_SESSION_TYPE_CLIENT_INITIATED_HANDS_FREE_ACTIVATION) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot start client-initiated OMA session: "
+ "invalid session type specified (%s)",
+ mm_oma_session_type_get_string (ctx->session_type));
+ handle_start_client_initiated_session_context_free (ctx);
+ return;
+ }
+
+ mm_dbg ("Starting client-initiated OMA session (%s)",
+ mm_oma_session_type_get_string (ctx->session_type));
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->start_client_initiated_session (
+ ctx->self,
+ ctx->session_type,
+ (GAsyncReadyCallback)start_client_initiated_session_ready,
+ ctx);
+}
+
+static gboolean
+handle_start_client_initiated_session (MmGdbusModemOma *skeleton,
+ GDBusMethodInvocation *invocation,
+ guint32 session_type,
+ MMIfaceModemOma *self)
+{
+ HandleStartClientInitiatedSessionContext *ctx;
+
+ ctx = g_slice_new0 (HandleStartClientInitiatedSessionContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->session_type = session_type;
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_start_client_initiated_session_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Handle AcceptNetworkInitiatedSession() */
+
+typedef struct {
+ MmGdbusModemOma *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModemOma *self;
+ MMOmaSessionType session_type;
+ guint session_id;
+ gboolean accept;
+} HandleAcceptNetworkInitiatedSessionContext;
+
+static void
+handle_accept_network_initiated_session_context_free (HandleAcceptNetworkInitiatedSessionContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleAcceptNetworkInitiatedSessionContext, ctx);
+}
+
+static void
+accept_network_initiated_session_ready (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ HandleAcceptNetworkInitiatedSessionContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->accept_network_initiated_session_finish (self, res, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else {
+ /* If accepted or rejected, remove from pending */
+ add_or_remove_pending_network_initiated_session (self, FALSE, ctx->session_type, ctx->session_id);
+
+ /* If accepted, set as current */
+ if (ctx->accept) {
+ mm_gdbus_modem_oma_set_session_type (ctx->skeleton, ctx->session_type);
+ mm_iface_modem_oma_update_session_state (self, MM_OMA_SESSION_STATE_STARTED, MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN);
+ }
+
+ mm_gdbus_modem_oma_complete_accept_network_initiated_session (ctx->skeleton, ctx->invocation);
+ }
+
+ handle_accept_network_initiated_session_context_free (ctx);
+}
+
+static MMOmaSessionType
+get_pending_network_initiated_session_type (MMIfaceModemOma *self,
+ guint session_id)
+{
+ MMOmaSessionType session_type = MM_OMA_SESSION_TYPE_UNKNOWN;
+ MmGdbusModemOma *skeleton;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_OMA_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (skeleton) {
+ GArray *array;
+ guint i;
+
+ array = (mm_common_oma_pending_network_initiated_sessions_variant_to_garray (
+ mm_gdbus_modem_oma_get_pending_network_initiated_sessions (skeleton)));
+ for (i = 0; i < array->len && session_type == MM_OMA_SESSION_TYPE_UNKNOWN; i++) {
+ MMOmaPendingNetworkInitiatedSession *session;
+
+ session = &g_array_index (array, MMOmaPendingNetworkInitiatedSession, i);
+ if (session->session_id == session_id)
+ session_type = session->session_type;
+ }
+
+ g_array_unref (array);
+ g_object_unref (skeleton);
+ }
+
+ return session_type;
+}
+
+static void
+handle_accept_network_initiated_session_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleAcceptNetworkInitiatedSessionContext *ctx)
+{
+ GError *error = NULL;
+ MMModemState modem_state;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_accept_network_initiated_session_context_free (ctx);
+ return;
+ }
+
+ modem_state = MM_MODEM_STATE_UNKNOWN;
+ g_object_get (self,
+ MM_IFACE_MODEM_STATE, &modem_state,
+ NULL);
+ if (modem_state < MM_MODEM_STATE_ENABLED) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cannot accept network-initiated OMA session: "
+ "device not yet enabled");
+ handle_accept_network_initiated_session_context_free (ctx);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->accept_network_initiated_session ||
+ !MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->accept_network_initiated_session_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot accept network-initiated OMA session: "
+ "operation not supported");
+ handle_accept_network_initiated_session_context_free (ctx);
+ return;
+ }
+
+ ctx->session_type = get_pending_network_initiated_session_type (ctx->self, ctx->session_id);
+ if (ctx->session_type == MM_OMA_SESSION_TYPE_UNKNOWN) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot accept network-initiated OMA session: "
+ "unknown session id (%u)",
+ ctx->session_id);
+ handle_accept_network_initiated_session_context_free (ctx);
+ return;
+ }
+
+ mm_dbg ("%s network-initiated OMA session (%s, %u)",
+ ctx->accept ? "Accepting" : "Rejecting",
+ mm_oma_session_type_get_string (ctx->session_type),
+ ctx->session_id);
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->accept_network_initiated_session (
+ ctx->self,
+ ctx->session_id,
+ ctx->accept,
+ (GAsyncReadyCallback)accept_network_initiated_session_ready,
+ ctx);
+}
+
+static gboolean
+handle_accept_network_initiated_session (MmGdbusModemOma *skeleton,
+ GDBusMethodInvocation *invocation,
+ guint32 session_id,
+ gboolean accept,
+ MMIfaceModemOma *self)
+{
+ HandleAcceptNetworkInitiatedSessionContext *ctx;
+
+ ctx = g_slice_new0 (HandleAcceptNetworkInitiatedSessionContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->session_type = MM_OMA_SESSION_TYPE_UNKNOWN;
+ ctx->session_id = session_id;
+ ctx->accept = accept;
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_accept_network_initiated_session_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Handle CancelSession() */
+
+typedef struct {
+ MmGdbusModemOma *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModemOma *self;
+} HandleCancelSessionContext;
+
+static void
+handle_cancel_session_context_free (HandleCancelSessionContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleCancelSessionContext, ctx);
+}
+
+static void
+cancel_session_ready (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ HandleCancelSessionContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cancel_session_finish (self, res, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else {
+ /* Clear interface info when cancelled */
+ mm_gdbus_modem_oma_set_session_type (ctx->skeleton, MM_OMA_SESSION_TYPE_UNKNOWN);
+ mm_iface_modem_oma_update_session_state (self, MM_OMA_SESSION_STATE_UNKNOWN, MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN);
+
+ mm_gdbus_modem_oma_complete_cancel_session (ctx->skeleton, ctx->invocation);
+ }
+
+ handle_cancel_session_context_free (ctx);
+}
+
+static void
+handle_cancel_session_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleCancelSessionContext *ctx)
+{
+ GError *error = NULL;
+ MMModemState modem_state;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_cancel_session_context_free (ctx);
+ return;
+ }
+
+ modem_state = MM_MODEM_STATE_UNKNOWN;
+ g_object_get (self,
+ MM_IFACE_MODEM_STATE, &modem_state,
+ NULL);
+ if (modem_state < MM_MODEM_STATE_ENABLED) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cannot cancel OMA session: "
+ "device not yet enabled");
+ handle_cancel_session_context_free (ctx);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cancel_session ||
+ !MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cancel_session_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot cancel OMA session: "
+ "operation not supported");
+ handle_cancel_session_context_free (ctx);
+ return;
+ }
+
+ mm_dbg ("Cancelling OMA session");
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cancel_session (
+ ctx->self,
+ (GAsyncReadyCallback)cancel_session_ready,
+ ctx);
+}
+
+static gboolean
+handle_cancel_session (MmGdbusModemOma *skeleton,
+ GDBusMethodInvocation *invocation,
+ MMIfaceModemOma *self)
+{
+ HandleCancelSessionContext *ctx;
+
+ ctx = g_slice_new0 (HandleCancelSessionContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_cancel_session_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct _DisablingContext DisablingContext;
+static void interface_disabling_step (DisablingContext *ctx);
+
+typedef enum {
+ DISABLING_STEP_FIRST,
+ DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS,
+ DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS,
+ DISABLING_STEP_LAST
+} DisablingStep;
+
+struct _DisablingContext {
+ MMIfaceModemOma *self;
+ DisablingStep step;
+ GSimpleAsyncResult *result;
+ MmGdbusModemOma *skeleton;
+};
+
+static void
+disabling_context_complete_and_free (DisablingContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+ g_object_unref (ctx->self);
+ g_object_unref (ctx->result);
+ if (ctx->skeleton)
+ g_object_unref (ctx->skeleton);
+ g_free (ctx);
+}
+
+gboolean
+mm_iface_modem_oma_disable_finish (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+disable_unsolicited_events_ready (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ DisablingContext *ctx)
+{
+ GError *error = NULL;
+
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->disable_unsolicited_events_finish (self, res, &error);
+ if (error) {
+ g_simple_async_result_take_error (ctx->result, error);
+ disabling_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_disabling_step (ctx);
+}
+
+static void
+cleanup_unsolicited_events_ready (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ DisablingContext *ctx)
+{
+ GError *error = NULL;
+
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->cleanup_unsolicited_events_finish (self, res, &error);
+ if (error) {
+ g_simple_async_result_take_error (ctx->result, error);
+ disabling_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_disabling_step (ctx);
+}
+
+static void
+interface_disabling_step (DisablingContext *ctx)
+{
+ switch (ctx->step) {
+ case DISABLING_STEP_FIRST:
+ /* Fall down to next step */
+ ctx->step++;
+
+ case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS:
+ /* Allow cleaning up unsolicited events */
+ if (MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->disable_unsolicited_events &&
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->disable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->disable_unsolicited_events (
+ ctx->self,
+ (GAsyncReadyCallback)disable_unsolicited_events_ready,
+ ctx);
+ return;
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
+ case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS:
+ /* Allow cleaning up unsolicited events */
+ if (MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events &&
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events (
+ ctx->self,
+ (GAsyncReadyCallback)cleanup_unsolicited_events_ready,
+ ctx);
+ return;
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
+ case DISABLING_STEP_LAST:
+ /* We are done without errors! */
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ disabling_context_complete_and_free (ctx);
+ return;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_iface_modem_oma_disable (MMIfaceModemOma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ DisablingContext *ctx;
+
+ ctx = g_new0 (DisablingContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ mm_iface_modem_oma_disable);
+ ctx->step = DISABLING_STEP_FIRST;
+ g_object_get (ctx->self,
+ MM_IFACE_MODEM_OMA_DBUS_SKELETON, &ctx->skeleton,
+ NULL);
+ if (!ctx->skeleton) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ disabling_context_complete_and_free (ctx);
+ return;
+ }
+
+ interface_disabling_step (ctx);
+}
+
+/*****************************************************************************/
+
+typedef struct _EnablingContext EnablingContext;
+static void interface_enabling_step (EnablingContext *ctx);
+
+typedef enum {
+ ENABLING_STEP_FIRST,
+ ENABLING_STEP_LOAD_FEATURES,
+ ENABLING_STEP_SETUP_UNSOLICITED_EVENTS,
+ ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS,
+ ENABLING_STEP_LAST
+} EnablingStep;
+
+struct _EnablingContext {
+ MMIfaceModemOma *self;
+ EnablingStep step;
+ GSimpleAsyncResult *result;
+ GCancellable *cancellable;
+ MmGdbusModemOma *skeleton;
+};
+
+static void
+enabling_context_complete_and_free (EnablingContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+ g_object_unref (ctx->self);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->cancellable);
+ if (ctx->skeleton)
+ g_object_unref (ctx->skeleton);
+ g_free (ctx);
+}
+
+static gboolean
+enabling_context_complete_and_free_if_cancelled (EnablingContext *ctx)
+{
+ if (!g_cancellable_is_cancelled (ctx->cancellable))
+ return FALSE;
+
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_CANCELLED,
+ "Interface enabling cancelled");
+ enabling_context_complete_and_free (ctx);
+ return TRUE;
+}
+
+gboolean
+mm_iface_modem_oma_enable_finish (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+load_features_ready (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ EnablingContext *ctx)
+{
+ GError *error = NULL;
+ MMOmaFeature features;
+
+ features = MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->load_features_finish (self, res, &error);
+ if (error) {
+ g_simple_async_result_take_error (ctx->result, error);
+ enabling_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Update in the interface */
+ mm_gdbus_modem_oma_set_features (ctx->skeleton, features);
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_enabling_step (ctx);
+}
+
+static void
+setup_unsolicited_events_ready (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ EnablingContext *ctx)
+{
+ GError *error = NULL;
+
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error);
+ if (error) {
+ g_simple_async_result_take_error (ctx->result, error);
+ enabling_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_enabling_step (ctx);
+}
+
+static void
+enable_unsolicited_events_ready (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ EnablingContext *ctx)
+{
+ GError *error = NULL;
+
+ /* Not critical! */
+ if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error)) {
+ mm_dbg ("Couldn't enable unsolicited events: '%s'", error->message);
+ g_error_free (error);
+ }
+
+ /* Go on with next step */
+ ctx->step++;
+ interface_enabling_step (ctx);
+}
+
+static void
+interface_enabling_step (EnablingContext *ctx)
+{
+ /* Don't run new steps if we're cancelled */
+ if (enabling_context_complete_and_free_if_cancelled (ctx))
+ return;
+
+ switch (ctx->step) {
+ case ENABLING_STEP_FIRST:
+ /* Fall down to next step */
+ ctx->step++;
+
+ case ENABLING_STEP_LOAD_FEATURES:
+ if (MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->load_features &&
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->load_features_finish) {
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->load_features (
+ ctx->self,
+ (GAsyncReadyCallback)load_features_ready,
+ ctx);
+ return;
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
+ case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS:
+ /* Allow setting up unsolicited events */
+ if (MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup_unsolicited_events &&
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup_unsolicited_events (
+ ctx->self,
+ (GAsyncReadyCallback)setup_unsolicited_events_ready,
+ ctx);
+ return;
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
+ case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS:
+ /* Allow setting up unsolicited events */
+ if (MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->enable_unsolicited_events &&
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->enable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->enable_unsolicited_events (
+ ctx->self,
+ (GAsyncReadyCallback)enable_unsolicited_events_ready,
+ ctx);
+ return;
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
+ case ENABLING_STEP_LAST:
+ /* We are done without errors! */
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ enabling_context_complete_and_free (ctx);
+ return;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_iface_modem_oma_enable (MMIfaceModemOma *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ EnablingContext *ctx;
+
+ ctx = g_new0 (EnablingContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->cancellable = g_object_ref (cancellable);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ mm_iface_modem_oma_enable);
+ ctx->step = ENABLING_STEP_FIRST;
+ g_object_get (ctx->self,
+ MM_IFACE_MODEM_OMA_DBUS_SKELETON, &ctx->skeleton,
+ NULL);
+ if (!ctx->skeleton) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ enabling_context_complete_and_free (ctx);
+ return;
+ }
+
+ interface_enabling_step (ctx);
+}
+
+/*****************************************************************************/
+
+typedef struct _InitializationContext InitializationContext;
+static void interface_initialization_step (InitializationContext *ctx);
+
+typedef enum {
+ INITIALIZATION_STEP_FIRST,
+ INITIALIZATION_STEP_CHECK_SUPPORT,
+ INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED,
+ INITIALIZATION_STEP_LAST
+} InitializationStep;
+
+struct _InitializationContext {
+ MMIfaceModemOma *self;
+ MmGdbusModemOma *skeleton;
+ GSimpleAsyncResult *result;
+ GCancellable *cancellable;
+ InitializationStep step;
+};
+
+static void
+initialization_context_complete_and_free (InitializationContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+ g_object_unref (ctx->self);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->cancellable);
+ g_object_unref (ctx->skeleton);
+ g_free (ctx);
+}
+
+static gboolean
+initialization_context_complete_and_free_if_cancelled (InitializationContext *ctx)
+{
+ if (!g_cancellable_is_cancelled (ctx->cancellable))
+ return FALSE;
+
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_CANCELLED,
+ "Interface initialization cancelled");
+ initialization_context_complete_and_free (ctx);
+ return TRUE;
+}
+
+static void
+check_support_ready (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ InitializationContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->check_support_finish (self, res, &error)) {
+ if (error) {
+ /* This error shouldn't be treated as critical */
+ mm_dbg ("OMA support check failed: '%s'", error->message);
+ g_error_free (error);
+ }
+ } else {
+ /* OMA is supported! */
+ g_object_set_qdata (G_OBJECT (self),
+ supported_quark,
+ GUINT_TO_POINTER (TRUE));
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_initialization_step (ctx);
+}
+
+static void
+interface_initialization_step (InitializationContext *ctx)
+{
+ /* Don't run new steps if we're cancelled */
+ if (initialization_context_complete_and_free_if_cancelled (ctx))
+ return;
+
+ switch (ctx->step) {
+ case INITIALIZATION_STEP_FIRST:
+ /* Setup quarks if we didn't do it before */
+ if (G_UNLIKELY (!support_checked_quark))
+ support_checked_quark = (g_quark_from_static_string (
+ SUPPORT_CHECKED_TAG));
+ if (G_UNLIKELY (!supported_quark))
+ supported_quark = (g_quark_from_static_string (
+ SUPPORTED_TAG));
+
+ /* Fall down to next step */
+ ctx->step++;
+
+ case INITIALIZATION_STEP_CHECK_SUPPORT:
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self),
+ support_checked_quark))) {
+ /* Set the checked flag so that we don't run it again */
+ g_object_set_qdata (G_OBJECT (ctx->self),
+ support_checked_quark,
+ GUINT_TO_POINTER (TRUE));
+ /* Initially, assume we don't support it */
+ g_object_set_qdata (G_OBJECT (ctx->self),
+ supported_quark,
+ GUINT_TO_POINTER (FALSE));
+
+ if (MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->check_support &&
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->check_support_finish) {
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->check_support (
+ ctx->self,
+ (GAsyncReadyCallback)check_support_ready,
+ ctx);
+ return;
+ }
+
+ /* If there is no implementation to check support, assume we DON'T
+ * support it. */
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
+ case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED:
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self),
+ supported_quark))) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "OMA not supported");
+ initialization_context_complete_and_free (ctx);
+ return;
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
+ case INITIALIZATION_STEP_LAST:
+ /* We are done without errors! */
+
+ /* Handle method invocations */
+ g_signal_connect (ctx->skeleton,
+ "handle-setup",
+ G_CALLBACK (handle_setup),
+ ctx->self);
+ g_signal_connect (ctx->skeleton,
+ "handle-start-client-initiated-session",
+ G_CALLBACK (handle_start_client_initiated_session),
+ ctx->self);
+ g_signal_connect (ctx->skeleton,
+ "handle-accept-network-initiated-session",
+ G_CALLBACK (handle_accept_network_initiated_session),
+ ctx->self);
+ g_signal_connect (ctx->skeleton,
+ "handle-cancel-session",
+ G_CALLBACK (handle_cancel_session),
+ ctx->self);
+
+ /* Finally, export the new interface */
+ mm_gdbus_object_skeleton_set_modem_oma (MM_GDBUS_OBJECT_SKELETON (ctx->self),
+ MM_GDBUS_MODEM_OMA (ctx->skeleton));
+
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ initialization_context_complete_and_free (ctx);
+ return;
+ }
+
+ g_assert_not_reached ();
+}
+
+gboolean
+mm_iface_modem_oma_initialize_finish (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+void
+mm_iface_modem_oma_initialize (MMIfaceModemOma *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ InitializationContext *ctx;
+ MmGdbusModemOma *skeleton = NULL;
+
+ /* Did we already create it? */
+ g_object_get (self,
+ MM_IFACE_MODEM_OMA_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (!skeleton) {
+ skeleton = mm_gdbus_modem_oma_skeleton_new ();
+ g_object_set (self,
+ MM_IFACE_MODEM_OMA_DBUS_SKELETON, skeleton,
+ NULL);
+
+ /* Set all initial property defaults */
+ mm_gdbus_modem_oma_set_features (skeleton, MM_OMA_FEATURE_NONE);
+ mm_gdbus_modem_oma_set_session_type (skeleton, MM_OMA_SESSION_TYPE_UNKNOWN);
+ mm_gdbus_modem_oma_set_session_state (skeleton, MM_OMA_SESSION_STATE_UNKNOWN);
+ mm_gdbus_modem_oma_set_pending_network_initiated_sessions (skeleton, mm_common_build_oma_pending_network_initiated_sessions_default ());
+ }
+
+ /* Perform async initialization here */
+
+ ctx = g_new0 (InitializationContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->cancellable = g_object_ref (cancellable);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ mm_iface_modem_oma_initialize);
+ ctx->step = INITIALIZATION_STEP_FIRST;
+ ctx->skeleton = skeleton;
+
+ interface_initialization_step (ctx);
+}
+
+void
+mm_iface_modem_oma_shutdown (MMIfaceModemOma *self)
+{
+ /* Unexport DBus interface and remove the skeleton */
+ mm_gdbus_object_skeleton_set_modem_oma (MM_GDBUS_OBJECT_SKELETON (self), NULL);
+ g_object_set (self,
+ MM_IFACE_MODEM_OMA_DBUS_SKELETON, NULL,
+ NULL);
+}
+
+/*****************************************************************************/
+
+static void
+iface_modem_oma_init (gpointer g_iface)
+{
+ static gboolean initialized = FALSE;
+
+ if (initialized)
+ return;
+
+ /* Properties */
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_object (MM_IFACE_MODEM_OMA_DBUS_SKELETON,
+ "OMA DBus skeleton",
+ "DBus skeleton for the OMA interface",
+ MM_GDBUS_TYPE_MODEM_OMA_SKELETON,
+ G_PARAM_READWRITE));
+
+ initialized = TRUE;
+}
+
+GType
+mm_iface_modem_oma_get_type (void)
+{
+ static GType iface_modem_oma_type = 0;
+
+ if (!G_UNLIKELY (iface_modem_oma_type)) {
+ static const GTypeInfo info = {
+ sizeof (MMIfaceModemOma), /* class_size */
+ iface_modem_oma_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ iface_modem_oma_type = g_type_register_static (G_TYPE_INTERFACE,
+ "MMIfaceModemOma",
+ &info,
+ 0);
+
+ g_type_interface_add_prerequisite (iface_modem_oma_type, MM_TYPE_IFACE_MODEM);
+ }
+
+ return iface_modem_oma_type;
+}
diff --git a/src/mm-iface-modem-oma.h b/src/mm-iface-modem-oma.h
new file mode 100644
index 0000000..3e3d00b
--- /dev/null
+++ b/src/mm-iface-modem-oma.h
@@ -0,0 +1,164 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2013 Google, Inc.
+ */
+
+#ifndef MM_IFACE_MODEM_OMA_H
+#define MM_IFACE_MODEM_OMA_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define MM_TYPE_IFACE_MODEM_OMA (mm_iface_modem_oma_get_type ())
+#define MM_IFACE_MODEM_OMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_OMA, MMIfaceModemOma))
+#define MM_IS_IFACE_MODEM_OMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_OMA))
+#define MM_IFACE_MODEM_OMA_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_OMA, MMIfaceModemOma))
+
+#define MM_IFACE_MODEM_OMA_DBUS_SKELETON "iface-modem-oma-dbus-skeleton"
+
+typedef struct _MMIfaceModemOma MMIfaceModemOma;
+
+struct _MMIfaceModemOma {
+ GTypeInterface g_iface;
+
+ /* Check for Oma support (async) */
+ void (* check_support) (MMIfaceModemOma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* check_support_finish) (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous setting up unsolicited events */
+ void (* setup_unsolicited_events) (MMIfaceModemOma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* setup_unsolicited_events_finish) (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous cleaning up of unsolicited events */
+ void (* cleanup_unsolicited_events) (MMIfaceModemOma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* cleanup_unsolicited_events_finish) (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous enabling unsolicited events */
+ void (* enable_unsolicited_events) (MMIfaceModemOma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* enable_unsolicited_events_finish) (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous disabling unsolicited events */
+ void (* disable_unsolicited_events) (MMIfaceModemOma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* disable_unsolicited_events_finish) (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Get current features */
+ void (* load_features) (MMIfaceModemOma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ MMOmaFeature (* load_features_finish) (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Setup */
+ void (* setup) (MMIfaceModemOma *self,
+ MMOmaFeature features,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* setup_finish) (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Start client-initiated session */
+ void (* start_client_initiated_session) (MMIfaceModemOma *self,
+ MMOmaSessionType session_type,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* start_client_initiated_session_finish) (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Accept network-initiated session */
+ void (* accept_network_initiated_session) (MMIfaceModemOma *self,
+ guint session_id,
+ gboolean accept,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* accept_network_initiated_session_finish) (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Cancel session */
+ void (* cancel_session) (MMIfaceModemOma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* cancel_session_finish) (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error);
+};
+
+GType mm_iface_modem_oma_get_type (void);
+
+/* Initialize Oma interface (async) */
+void mm_iface_modem_oma_initialize (MMIfaceModemOma *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_oma_initialize_finish (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Enable Oma interface (async) */
+void mm_iface_modem_oma_enable (MMIfaceModemOma *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_oma_enable_finish (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Disable Oma interface (async) */
+void mm_iface_modem_oma_disable (MMIfaceModemOma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_oma_disable_finish (MMIfaceModemOma *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Shutdown Oma interface */
+void mm_iface_modem_oma_shutdown (MMIfaceModemOma *self);
+
+/* Bind properties for simple GetStatus() */
+void mm_iface_modem_oma_bind_simple_status (MMIfaceModemOma *self,
+ MMSimpleStatus *status);
+
+/* Report new pending network-initiated session */
+void mm_iface_modem_oma_add_pending_network_initiated_session (MMIfaceModemOma *self,
+ MMOmaSessionType session_type,
+ guint session_id);
+
+/* Report new session state */
+void mm_iface_modem_oma_update_session_state (MMIfaceModemOma *self,
+ MMOmaSessionState session_state,
+ MMOmaSessionStateFailedReason session_state_failed_reason);
+
+#endif /* MM_IFACE_MODEM_OMA_H */
diff --git a/src/mm-iface-modem-signal.c b/src/mm-iface-modem-signal.c
new file mode 100644
index 0000000..d28c2ad
--- /dev/null
+++ b/src/mm-iface-modem-signal.c
@@ -0,0 +1,612 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-signal.h"
+#include "mm-log.h"
+
+#define SUPPORT_CHECKED_TAG "signal-support-checked-tag"
+#define SUPPORTED_TAG "signal-supported-tag"
+#define REFRESH_CONTEXT_TAG "signal-refresh-context-tag"
+
+static GQuark support_checked_quark;
+static GQuark supported_quark;
+static GQuark refresh_context_quark;
+
+/*****************************************************************************/
+
+void
+mm_iface_modem_signal_bind_simple_status (MMIfaceModemSignal *self,
+ MMSimpleStatus *status)
+{
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ guint rate;
+ guint timeout_source;
+} RefreshContext;
+
+static void
+refresh_context_free (RefreshContext *ctx)
+{
+ if (ctx->timeout_source)
+ g_source_remove (ctx->timeout_source);
+ g_slice_free (RefreshContext, ctx);
+}
+
+static void
+clear_values (MMIfaceModemSignal *self)
+{
+ MmGdbusModemSignal *skeleton;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (!skeleton)
+ return;
+
+ mm_gdbus_modem_signal_set_cdma (skeleton, NULL);
+ mm_gdbus_modem_signal_set_evdo (skeleton, NULL);
+ mm_gdbus_modem_signal_set_gsm (skeleton, NULL);
+ mm_gdbus_modem_signal_set_umts (skeleton, NULL);
+ mm_gdbus_modem_signal_set_lte (skeleton, NULL);
+ g_object_unref (skeleton);
+}
+
+static void
+load_values_ready (MMIfaceModemSignal *self,
+ GAsyncResult *res)
+{
+ GVariant *dictionary;
+ GError *error = NULL;
+ MMSignal *cdma = NULL;
+ MMSignal *evdo = NULL;
+ MMSignal *gsm = NULL;
+ MMSignal *umts = NULL;
+ MMSignal *lte = NULL;
+ MmGdbusModemSignal *skeleton;
+
+ if (!MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->load_values_finish (
+ self,
+ res,
+ &cdma,
+ &evdo,
+ &gsm,
+ &umts,
+ &lte,
+ &error)) {
+ mm_warn ("Couldn't load extended signal information: %s", error->message);
+ g_error_free (error);
+ clear_values (self);
+ return;
+ }
+
+ g_object_get (self,
+ MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (!skeleton) {
+ mm_warn ("Cannot update extended signal information: "
+ "Couldn't get interface skeleton");
+ return;
+ }
+
+ if (cdma) {
+ dictionary = mm_signal_get_dictionary (cdma);
+ mm_gdbus_modem_signal_set_cdma (skeleton, dictionary);
+ g_variant_unref (dictionary);
+ g_object_unref (cdma);
+ }
+
+ if (evdo) {
+ dictionary = mm_signal_get_dictionary (evdo);
+ mm_gdbus_modem_signal_set_evdo (skeleton, dictionary);
+ g_variant_unref (dictionary);
+ g_object_unref (evdo);
+ }
+
+ if (gsm) {
+ dictionary = mm_signal_get_dictionary (gsm);
+ mm_gdbus_modem_signal_set_gsm (skeleton, dictionary);
+ g_variant_unref (dictionary);
+ g_object_unref (gsm);
+ }
+
+ if (umts) {
+ dictionary = mm_signal_get_dictionary (umts);
+ mm_gdbus_modem_signal_set_umts (skeleton, dictionary);
+ g_variant_unref (dictionary);
+ g_object_unref (umts);
+ }
+
+ if (lte) {
+ dictionary = mm_signal_get_dictionary (lte);
+ mm_gdbus_modem_signal_set_lte (skeleton, dictionary);
+ g_variant_unref (dictionary);
+ g_object_unref (lte);
+ }
+
+ /* Flush right away */
+ g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton));
+
+ g_object_unref (skeleton);
+}
+
+static gboolean
+refresh_context_cb (MMIfaceModemSignal *self)
+{
+ MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->load_values (
+ self,
+ NULL,
+ (GAsyncReadyCallback)load_values_ready,
+ NULL);
+ return TRUE;
+}
+
+static void
+teardown_refresh_context (MMIfaceModemSignal *self)
+{
+ mm_dbg ("Extended signal information reporting disabled");
+ clear_values (self);
+ if (G_UNLIKELY (!refresh_context_quark))
+ refresh_context_quark = g_quark_from_static_string (REFRESH_CONTEXT_TAG);
+ g_object_set_qdata (G_OBJECT (self), refresh_context_quark, NULL);
+}
+
+static gboolean
+setup_refresh_context (MMIfaceModemSignal *self,
+ gboolean update_rate,
+ guint new_rate,
+ GError **error)
+{
+ MmGdbusModemSignal *skeleton;
+ RefreshContext *ctx;
+ MMModemState modem_state;
+
+ if (G_UNLIKELY (!refresh_context_quark))
+ refresh_context_quark = g_quark_from_static_string (REFRESH_CONTEXT_TAG);
+
+ g_object_get (self,
+ MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON, &skeleton,
+ MM_IFACE_MODEM_STATE, &modem_state,
+ NULL);
+ if (!skeleton) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ return FALSE;
+ }
+
+ if (update_rate)
+ mm_gdbus_modem_signal_set_rate (skeleton, new_rate);
+ else
+ new_rate = mm_gdbus_modem_signal_get_rate (skeleton);
+ g_object_unref (skeleton);
+
+ /* User disabling? */
+ if (new_rate == 0) {
+ mm_dbg ("Extended signal information reporting disabled (rate: 0 seconds)");
+ clear_values (self);
+ g_object_set_qdata (G_OBJECT (self), refresh_context_quark, NULL);
+ return TRUE;
+ }
+
+ if (modem_state < MM_MODEM_STATE_ENABLING) {
+ mm_dbg ("Extended signal information reporting disabled (modem not yet enabled)");
+ return TRUE;
+ }
+
+ /* Setup refresh context */
+ ctx = g_object_get_qdata (G_OBJECT (self), refresh_context_quark);
+ if (!ctx) {
+ ctx = g_slice_new0 (RefreshContext);
+ g_object_set_qdata_full (G_OBJECT (self),
+ refresh_context_quark,
+ ctx,
+ (GDestroyNotify)refresh_context_free);
+ }
+
+ /* We're enabling, compare to old rate */
+ if (ctx->rate == new_rate) {
+ /* Already there */
+ return TRUE;
+ }
+
+ /* Update refresh context */
+ mm_dbg ("Extended signal information reporting enabled (rate: %u seconds)", new_rate);
+ ctx->rate = new_rate;
+ if (ctx->timeout_source)
+ g_source_remove (ctx->timeout_source);
+ ctx->timeout_source = g_timeout_add_seconds (ctx->rate, (GSourceFunc) refresh_context_cb, self);
+
+ /* Also launch right away */
+ refresh_context_cb (self);
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ GDBusMethodInvocation *invocation;
+ MmGdbusModemSignal *skeleton;
+ MMIfaceModemSignal *self;
+ guint rate;
+} HandleSetupContext;
+
+static void
+handle_setup_context_free (HandleSetupContext *ctx)
+{
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleSetupContext, ctx);
+}
+
+static void
+handle_setup_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleSetupContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else if (!setup_refresh_context (ctx->self, TRUE, ctx->rate, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else
+ mm_gdbus_modem_signal_complete_setup (ctx->skeleton, ctx->invocation);
+ handle_setup_context_free (ctx);
+}
+
+static gboolean
+handle_setup (MmGdbusModemSignal *skeleton,
+ GDBusMethodInvocation *invocation,
+ guint rate,
+ MMIfaceModemSignal *self)
+{
+ HandleSetupContext *ctx;
+
+ ctx = g_slice_new (HandleSetupContext);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->self = g_object_ref (self);
+ ctx->rate = rate;
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_setup_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_iface_modem_signal_disable_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+void
+mm_iface_modem_signal_disable (MMIfaceModemSignal *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ mm_iface_modem_signal_disable);
+
+ teardown_refresh_context (self);
+
+ g_simple_async_result_set_op_res_gboolean (result, TRUE);
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_iface_modem_signal_enable_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+void
+mm_iface_modem_signal_enable (MMIfaceModemSignal *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ GError *error = NULL;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ mm_iface_modem_signal_enable);
+
+ if (!setup_refresh_context (self, FALSE, 0, &error))
+ g_simple_async_result_take_error (result, error);
+ else
+ g_simple_async_result_set_op_res_gboolean (result, TRUE);
+
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+}
+
+/*****************************************************************************/
+
+typedef struct _InitializationContext InitializationContext;
+static void interface_initialization_step (InitializationContext *ctx);
+
+typedef enum {
+ INITIALIZATION_STEP_FIRST,
+ INITIALIZATION_STEP_CHECK_SUPPORT,
+ INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED,
+ INITIALIZATION_STEP_LAST
+} InitializationStep;
+
+struct _InitializationContext {
+ MMIfaceModemSignal *self;
+ MmGdbusModemSignal *skeleton;
+ GCancellable *cancellable;
+ GSimpleAsyncResult *result;
+ InitializationStep step;
+};
+
+static void
+initialization_context_complete_and_free (InitializationContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+ g_object_unref (ctx->self);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->cancellable);
+ g_object_unref (ctx->skeleton);
+ g_slice_free (InitializationContext, ctx);
+}
+
+
+gboolean
+mm_iface_modem_signal_initialize_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static gboolean
+initialization_context_complete_and_free_if_cancelled (InitializationContext *ctx)
+{
+ if (!g_cancellable_is_cancelled (ctx->cancellable))
+ return FALSE;
+
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_CANCELLED,
+ "Interface initialization cancelled");
+ initialization_context_complete_and_free (ctx);
+ return TRUE;
+}
+
+static void
+check_support_ready (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ InitializationContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->check_support_finish (self, res, &error)) {
+ if (error) {
+ /* This error shouldn't be treated as critical */
+ mm_dbg ("Extended signal support check failed: '%s'", error->message);
+ g_error_free (error);
+ }
+ } else {
+ /* Signal is supported! */
+ g_object_set_qdata (G_OBJECT (self),
+ supported_quark,
+ GUINT_TO_POINTER (TRUE));
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_initialization_step (ctx);
+}
+
+static void
+interface_initialization_step (InitializationContext *ctx)
+{
+ /* Don't run new steps if we're cancelled */
+ if (initialization_context_complete_and_free_if_cancelled (ctx))
+ return;
+
+ switch (ctx->step) {
+ case INITIALIZATION_STEP_FIRST:
+ /* Setup quarks if we didn't do it before */
+ if (G_UNLIKELY (!support_checked_quark))
+ support_checked_quark = (g_quark_from_static_string (
+ SUPPORT_CHECKED_TAG));
+ if (G_UNLIKELY (!supported_quark))
+ supported_quark = (g_quark_from_static_string (
+ SUPPORTED_TAG));
+
+ /* Fall down to next step */
+ ctx->step++;
+
+ case INITIALIZATION_STEP_CHECK_SUPPORT:
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self),
+ support_checked_quark))) {
+ /* Set the checked flag so that we don't run it again */
+ g_object_set_qdata (G_OBJECT (ctx->self),
+ support_checked_quark,
+ GUINT_TO_POINTER (TRUE));
+ /* Initially, assume we don't support it */
+ g_object_set_qdata (G_OBJECT (ctx->self),
+ supported_quark,
+ GUINT_TO_POINTER (FALSE));
+
+ if (MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (ctx->self)->check_support &&
+ MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (ctx->self)->check_support_finish) {
+ MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (ctx->self)->check_support (
+ ctx->self,
+ (GAsyncReadyCallback)check_support_ready,
+ ctx);
+ return;
+ }
+
+ /* If there is no implementation to check support, assume we DON'T
+ * support it. */
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
+ case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED:
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self),
+ supported_quark))) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Extended Signal information not supported");
+ initialization_context_complete_and_free (ctx);
+ return;
+ }
+ /* Fall down to next step */
+ ctx->step++;
+
+ case INITIALIZATION_STEP_LAST:
+ /* We are done without errors! */
+
+ /* Handle method invocations */
+ g_signal_connect (ctx->skeleton,
+ "handle-setup",
+ G_CALLBACK (handle_setup),
+ ctx->self);
+ /* Finally, export the new interface */
+ mm_gdbus_object_skeleton_set_modem_signal (MM_GDBUS_OBJECT_SKELETON (ctx->self),
+ MM_GDBUS_MODEM_SIGNAL (ctx->skeleton));
+
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ initialization_context_complete_and_free (ctx);
+ return;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_iface_modem_signal_initialize (MMIfaceModemSignal *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ InitializationContext *ctx;
+ MmGdbusModemSignal *skeleton = NULL;
+
+ /* Did we already create it? */
+ g_object_get (self,
+ MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (!skeleton) {
+ skeleton = mm_gdbus_modem_signal_skeleton_new ();
+ clear_values (self);
+ g_object_set (self,
+ MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON, skeleton,
+ NULL);
+ }
+
+ /* Perform async initialization here */
+
+ ctx = g_slice_new0 (InitializationContext);
+ ctx->self = g_object_ref (self);
+ ctx->cancellable = g_object_ref (cancellable);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ mm_iface_modem_signal_initialize);
+ ctx->step = INITIALIZATION_STEP_FIRST;
+ ctx->skeleton = skeleton;
+
+ interface_initialization_step (ctx);
+}
+
+void
+mm_iface_modem_signal_shutdown (MMIfaceModemSignal *self)
+{
+ /* Unexport DBus interface and remove the skeleton */
+ mm_gdbus_object_skeleton_set_modem_signal (MM_GDBUS_OBJECT_SKELETON (self), NULL);
+ g_object_set (self,
+ MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON, NULL,
+ NULL);
+}
+
+/*****************************************************************************/
+
+static void
+iface_modem_signal_init (gpointer g_iface)
+{
+ static gboolean initialized = FALSE;
+
+ if (initialized)
+ return;
+
+ /* Properties */
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_object (MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON,
+ "Signal DBus skeleton",
+ "DBus skeleton for the Signal interface",
+ MM_GDBUS_TYPE_MODEM_SIGNAL_SKELETON,
+ G_PARAM_READWRITE));
+
+ initialized = TRUE;
+}
+
+GType
+mm_iface_modem_signal_get_type (void)
+{
+ static GType iface_modem_signal_type = 0;
+
+ if (!G_UNLIKELY (iface_modem_signal_type)) {
+ static const GTypeInfo info = {
+ sizeof (MMIfaceModemSignal), /* class_size */
+ iface_modem_signal_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ iface_modem_signal_type = g_type_register_static (G_TYPE_INTERFACE,
+ "MMIfaceModemSignal",
+ &info,
+ 0);
+
+ g_type_interface_add_prerequisite (iface_modem_signal_type, MM_TYPE_IFACE_MODEM);
+ }
+
+ return iface_modem_signal_type;
+}
diff --git a/src/mm-iface-modem-signal.h b/src/mm-iface-modem-signal.h
new file mode 100644
index 0000000..1d49e5f
--- /dev/null
+++ b/src/mm-iface-modem-signal.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#ifndef MM_IFACE_MODEM_SIGNAL_H
+#define MM_IFACE_MODEM_SIGNAL_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#define MM_TYPE_IFACE_MODEM_SIGNAL (mm_iface_modem_signal_get_type ())
+#define MM_IFACE_MODEM_SIGNAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_SIGNAL, MMIfaceModemSignal))
+#define MM_IS_IFACE_MODEM_SIGNAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_SIGNAL))
+#define MM_IFACE_MODEM_SIGNAL_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_SIGNAL, MMIfaceModemSignal))
+
+#define MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON "iface-modem-signal-dbus-skeleton"
+
+typedef struct _MMIfaceModemSignal MMIfaceModemSignal;
+
+struct _MMIfaceModemSignal {
+ GTypeInterface g_iface;
+
+ /* Check for Messaging support (async) */
+ void (* check_support) (MMIfaceModemSignal *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*check_support_finish) (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Load all values */
+ void (* load_values) (MMIfaceModemSignal *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* load_values_finish) (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ MMSignal **cdma,
+ MMSignal **evdo,
+ MMSignal **gsm,
+ MMSignal **umts,
+ MMSignal **lte,
+ GError **error);
+};
+
+GType mm_iface_modem_signal_get_type (void);
+
+/* Initialize Signal interface (async) */
+void mm_iface_modem_signal_initialize (MMIfaceModemSignal *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_signal_initialize_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Enable Signal interface (async) */
+void mm_iface_modem_signal_enable (MMIfaceModemSignal *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_signal_enable_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Disable Signal interface (async) */
+void mm_iface_modem_signal_disable (MMIfaceModemSignal *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_signal_disable_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Shutdown Signal interface */
+void mm_iface_modem_signal_shutdown (MMIfaceModemSignal *self);
+
+/* Bind properties for simple GetStatus() */
+void mm_iface_modem_signal_bind_simple_status (MMIfaceModemSignal *self,
+ MMSimpleStatus *status);
+
+#endif /* MM_IFACE_MODEM_SIGNAL_H */
diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c
index f0f10ce..5edbbae 100644
--- a/src/mm-iface-modem.c
+++ b/src/mm-iface-modem.c
@@ -80,12 +80,12 @@ mm_iface_modem_bind_simple_status (MMIfaceModem *self,
/*****************************************************************************/
/* Helper method to wait for a final state */
-#define MODEM_STATE_IS_INTERMEDIATE(state) \
- (state == MM_MODEM_STATE_INITIALIZING || \
- state == MM_MODEM_STATE_INITIALIZING || \
- state == MM_MODEM_STATE_INITIALIZING || \
- state == MM_MODEM_STATE_INITIALIZING || \
- state == MM_MODEM_STATE_INITIALIZING)
+#define MODEM_STATE_IS_INTERMEDIATE(state) \
+ (state == MM_MODEM_STATE_INITIALIZING || \
+ state == MM_MODEM_STATE_DISABLING || \
+ state == MM_MODEM_STATE_ENABLING || \
+ state == MM_MODEM_STATE_DISCONNECTING || \
+ state == MM_MODEM_STATE_CONNECTING)
typedef struct {
MMIfaceModem *self;
@@ -98,9 +98,14 @@ typedef struct {
static void
wait_for_final_state_context_complete_and_free (WaitForFinalStateContext *ctx)
{
+ /* The callback associated with 'ctx->result' may update the modem state.
+ * Disconnect the signal handler for modem state changes before completing
+ * 'ctx->result' in order to prevent state_changed from being invoked, which
+ * invokes wait_for_final_state_context_complete_and_free (ctx) again. */
+ g_signal_handler_disconnect (ctx->self, ctx->state_changed_id);
+
g_simple_async_result_complete (ctx->result);
g_object_unref (ctx->result);
- g_signal_handler_disconnect (ctx->self, ctx->state_changed_id);
g_source_remove (ctx->state_changed_wait_id);
g_object_unref (ctx->self);
g_slice_free (WaitForFinalStateContext, ctx);
@@ -206,6 +211,169 @@ mm_iface_modem_wait_for_final_state (MMIfaceModem *self,
}
/*****************************************************************************/
+/* Helper method to load unlock required, considering retries */
+
+#define MAX_RETRIES 6
+
+typedef struct {
+ MMIfaceModem *self;
+ GSimpleAsyncResult *result;
+ guint retries;
+ guint pin_check_timeout_id;
+} InternalLoadUnlockRequiredContext;
+
+static void
+internal_load_unlock_required_context_complete_and_free (InternalLoadUnlockRequiredContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->self);
+ g_slice_free (InternalLoadUnlockRequiredContext, ctx);
+}
+
+static MMModemLock
+internal_load_unlock_required_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return MM_MODEM_LOCK_UNKNOWN;
+
+ return (MMModemLock) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+static void internal_load_unlock_required_context_step (InternalLoadUnlockRequiredContext *ctx);
+
+static gboolean
+load_unlock_required_again (InternalLoadUnlockRequiredContext *ctx)
+{
+ ctx->pin_check_timeout_id = 0;
+ /* Retry the step */
+ internal_load_unlock_required_context_step (ctx);
+ return FALSE;
+}
+
+static void
+load_unlock_required_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ InternalLoadUnlockRequiredContext *ctx)
+{
+ GError *error = NULL;
+ MMModemLock lock;
+
+ lock = MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required_finish (self, res, &error);
+ if (error) {
+ mm_dbg ("Couldn't check if unlock required: '%s'", error->message);
+
+ /* For several kinds of errors, just return them directly */
+ if (error->domain == MM_SERIAL_ERROR ||
+ g_error_matches (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_CANCELLED) ||
+ g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) ||
+ g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) ||
+ g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) {
+ g_simple_async_result_take_error (ctx->result, error);
+ internal_load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* For the remaining ones, retry if possible */
+ if (ctx->retries < MAX_RETRIES) {
+ ctx->retries++;
+ mm_dbg ("Retrying (%u) unlock required check", ctx->retries);
+
+ g_assert (ctx->pin_check_timeout_id == 0);
+ ctx->pin_check_timeout_id = g_timeout_add_seconds (2,
+ (GSourceFunc)load_unlock_required_again,
+ ctx);
+ g_error_free (error);
+ return;
+ }
+
+ /* If reached max retries and still reporting error... default to SIM error */
+ g_error_free (error);
+ g_simple_async_result_set_error (ctx->result,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE,
+ "Couldn't get SIM lock status after %u retries",
+ MAX_RETRIES);
+ internal_load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Got the lock value, return it */
+ g_simple_async_result_set_op_res_gpointer (ctx->result,
+ GUINT_TO_POINTER (lock),
+ NULL);
+ internal_load_unlock_required_context_complete_and_free (ctx);
+}
+
+static void
+internal_load_unlock_required_context_step (InternalLoadUnlockRequiredContext *ctx)
+{
+ g_assert (ctx->pin_check_timeout_id == 0);
+ MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required (
+ ctx->self,
+ (GAsyncReadyCallback)load_unlock_required_ready,
+ ctx);
+}
+
+static void
+internal_load_unlock_required (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ InternalLoadUnlockRequiredContext *ctx;
+
+ ctx = g_slice_new0 (InternalLoadUnlockRequiredContext);
+ ctx->self = g_object_ref (self);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ internal_load_unlock_required);
+
+ if (!MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required ||
+ !MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required_finish) {
+ /* Just assume that no lock is required */
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ internal_load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
+
+ internal_load_unlock_required_context_step (ctx);
+}
+
+/*****************************************************************************/
+
+static void
+bearer_list_updated (MMBearerList *bearer_list,
+ GParamSpec *pspec,
+ MMIfaceModem *self)
+{
+ MmGdbusModem *skeleton;
+ gchar **paths;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (!skeleton)
+ return;
+
+ paths = mm_bearer_list_get_paths (bearer_list);
+ mm_gdbus_modem_set_bearers (skeleton, (const gchar *const *)paths);
+ g_strfreev (paths);
+
+ g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton));
+}
+
+/*****************************************************************************/
static MMModemState get_current_consolidated_state (MMIfaceModem *self, MMModemState modem_state);
@@ -1331,7 +1499,7 @@ get_updated_consolidated_state (MMIfaceModem *self,
/* Reported subsystem states will be REGISTRATION-related. This means
* that we would only expect a subset of the states being reported for
- * the subystem. Warn if we get others */
+ * the subsystem. Warn if we get others */
g_warn_if_fail (subsystem_state == MM_MODEM_STATE_ENABLED ||
subsystem_state == MM_MODEM_STATE_SEARCHING ||
subsystem_state == MM_MODEM_STATE_REGISTERED);
@@ -1626,7 +1794,6 @@ handle_reset_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleResetContext *ctx)
{
- MMModemState modem_state;
GError *error = NULL;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
@@ -1646,20 +1813,6 @@ handle_reset_auth_ready (MMBaseModem *self,
return;
}
- modem_state = MM_MODEM_STATE_UNKNOWN;
- g_object_get (self,
- MM_IFACE_MODEM_STATE, &modem_state,
- NULL);
-
- if (modem_state < MM_MODEM_STATE_DISABLED) {
- g_dbus_method_invocation_return_error (ctx->invocation,
- MM_CORE_ERROR,
- MM_CORE_ERROR_WRONG_STATE,
- "Cannot reset modem: not initialized/unlocked yet");
- handle_reset_context_free (ctx);
- return;
- }
-
MM_IFACE_MODEM_GET_INTERFACE (self)->reset (MM_IFACE_MODEM (self),
(GAsyncReadyCallback)handle_reset_ready,
ctx);
@@ -1725,7 +1878,6 @@ handle_factory_reset_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleFactoryResetContext *ctx)
{
- MMModemState modem_state;
GError *error = NULL;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
@@ -1746,21 +1898,6 @@ handle_factory_reset_auth_ready (MMBaseModem *self,
return;
}
- modem_state = MM_MODEM_STATE_UNKNOWN;
- g_object_get (self,
- MM_IFACE_MODEM_STATE, &modem_state,
- NULL);
-
- if (modem_state < MM_MODEM_STATE_DISABLED) {
- g_dbus_method_invocation_return_error (ctx->invocation,
- MM_CORE_ERROR,
- MM_CORE_ERROR_WRONG_STATE,
- "Cannot reset the modem to factory defaults: "
- "not initialized/unlocked yet");
- handle_factory_reset_context_free (ctx);
- return;
- }
-
MM_IFACE_MODEM_GET_INTERFACE (self)->factory_reset (MM_IFACE_MODEM (self),
ctx->code,
(GAsyncReadyCallback)handle_factory_reset_ready,
@@ -2398,7 +2535,7 @@ mm_iface_modem_set_current_modes (MMIfaceModem *self,
supported = mm_common_mode_combinations_variant_to_garray (
mm_gdbus_modem_get_supported_modes (ctx->skeleton));
- /* Don't allow mode switchin if only one item given in the supported list */
+ /* Don't allow mode switching if only one item given in the supported list */
if (supported->len == 1) {
g_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
@@ -2701,8 +2838,6 @@ typedef enum {
typedef struct {
MMIfaceModem *self;
UpdateLockInfoContextStep step;
- guint pin_check_tries;
- guint pin_check_timeout_id;
GSimpleAsyncResult *result;
MmGdbusModem *skeleton;
MMModemLock lock;
@@ -2775,23 +2910,14 @@ modem_after_sim_unlock_ready (MMIfaceModem *self,
update_lock_info_context_step (ctx);
}
-static gboolean
-load_unlock_required_again (UpdateLockInfoContext *ctx)
-{
- ctx->pin_check_timeout_id = 0;
- /* Retry the step */
- update_lock_info_context_step (ctx);
- return FALSE;
-}
-
static void
-load_unlock_required_ready (MMIfaceModem *self,
- GAsyncResult *res,
- UpdateLockInfoContext *ctx)
+internal_load_unlock_required_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ UpdateLockInfoContext *ctx)
{
GError *error = NULL;
- ctx->lock = MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required_finish (self, res, &error);
+ ctx->lock = internal_load_unlock_required_finish (self, res, &error);
if (error) {
/* Treat several SIM related, serial and other core errors as critical
* and abort the checks. These will end up moving the modem to a FAILED
@@ -2804,44 +2930,32 @@ load_unlock_required_ready (MMIfaceModem *self,
ctx->step = UPDATE_LOCK_INFO_CONTEXT_STEP_LAST;
update_lock_info_context_step (ctx);
return;
- } else if (g_error_matches (error,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) ||
- g_error_matches (error,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) ||
- g_error_matches (error,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) {
- if (mm_iface_modem_is_cdma (self)) {
- /* For mixed 3GPP+3GPP2 devices, skip SIM errors */
- mm_dbg ("Skipping SIM error in 3GPP2-capable device, assuming no lock is needed");
- g_error_free (error);
- ctx->lock = MM_MODEM_LOCK_NONE;
- } else {
- /* SIM errors are only critical in 3GPP-only devices */
+ }
+
+ if (g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) ||
+ g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) ||
+ g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) {
+ /* SIM errors are only critical in 3GPP-only devices */
+ if (!mm_iface_modem_is_cdma (self)) {
ctx->saved_error = error;
ctx->step = UPDATE_LOCK_INFO_CONTEXT_STEP_LAST;
update_lock_info_context_step (ctx);
return;
}
+
+ /* For mixed 3GPP+3GPP2 devices, skip SIM errors */
+ mm_dbg ("Skipping SIM error in 3GPP2-capable device, assuming no lock is needed");
+ g_error_free (error);
+ ctx->lock = MM_MODEM_LOCK_NONE;
} else {
mm_dbg ("Couldn't check if unlock required: '%s'", error->message);
g_error_free (error);
-
- /* Retry up to 6 times */
- if (mm_gdbus_modem_get_unlock_required (ctx->skeleton) != MM_MODEM_LOCK_NONE &&
- ++ctx->pin_check_tries < 6) {
- mm_dbg ("Retrying (%u) unlock required check", ctx->pin_check_tries);
- if (ctx->pin_check_timeout_id)
- g_source_remove (ctx->pin_check_timeout_id);
- ctx->pin_check_timeout_id = g_timeout_add_seconds (2,
- (GSourceFunc)load_unlock_required_again,
- ctx);
- return;
- }
-
- /* If reached max retries and still reporting error, set UNKNOWN */
ctx->lock = MM_MODEM_LOCK_UNKNOWN;
}
}
@@ -2873,12 +2987,10 @@ update_lock_info_context_step (UpdateLockInfoContext *ctx)
/* Don't re-ask if already known */
if (ctx->lock == MM_MODEM_LOCK_UNKNOWN) {
/* If we're already unlocked, we're done */
- if (mm_gdbus_modem_get_unlock_required (ctx->skeleton) != MM_MODEM_LOCK_NONE &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required (
+ if (mm_gdbus_modem_get_unlock_required (ctx->skeleton) != MM_MODEM_LOCK_NONE) {
+ internal_load_unlock_required (
ctx->self,
- (GAsyncReadyCallback)load_unlock_required_ready,
+ (GAsyncReadyCallback)internal_load_unlock_required_ready,
ctx);
return;
}
@@ -3561,6 +3673,12 @@ initialization_context_complete_and_free_if_cancelled (InitializationContext *ct
if (!g_cancellable_is_cancelled (ctx->cancellable))
return FALSE;
+ /* Simply ignore any fatal error encountered as the initialization is cancelled anyway. */
+ if (ctx->fatal_error) {
+ g_error_free (ctx->fatal_error);
+ ctx->fatal_error = NULL;
+ }
+
g_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
MM_CORE_ERROR_CANCELLED,
@@ -3617,13 +3735,13 @@ initialization_context_complete_and_free_if_cancelled (InitializationContext *ct
}
static void
-current_capabilities_load_unlock_required_ready (MMIfaceModem *self,
- GAsyncResult *res,
- InitializationContext *ctx)
+current_capabilities_internal_load_unlock_required_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ InitializationContext *ctx)
{
GError *error = NULL;
- MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required_finish (self, res, &error);
+ internal_load_unlock_required_finish (self, res, &error);
if (error) {
/* These SIM errors indicate that there is NO valid SIM available. So,
* remove all 3GPP caps from the current capabilities */
@@ -3682,13 +3800,11 @@ load_current_capabilities_ready (MMIfaceModem *self,
/* If the device is a multimode device (3GPP+3GPP2) check whether we have a
* SIM or not. */
if (caps & MM_MODEM_CAPABILITY_CDMA_EVDO &&
- (caps & MM_MODEM_CAPABILITY_GSM_UMTS || caps & MM_MODEM_CAPABILITY_LTE) &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required_finish) {
+ (caps & MM_MODEM_CAPABILITY_GSM_UMTS || caps & MM_MODEM_CAPABILITY_LTE)) {
mm_dbg ("Checking if multimode device has a SIM...");
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required (
+ internal_load_unlock_required (
ctx->self,
- (GAsyncReadyCallback)current_capabilities_load_unlock_required_ready,
+ (GAsyncReadyCallback)current_capabilities_internal_load_unlock_required_ready,
ctx);
return;
}
@@ -3877,6 +3993,21 @@ sim_reinit_ready (MMSim *sim,
interface_initialization_step (ctx);
}
+void
+mm_iface_modem_update_own_numbers (MMIfaceModem *self,
+ const GStrv own_numbers)
+{
+ MmGdbusModem *skeleton = NULL;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (skeleton) {
+ mm_gdbus_modem_set_own_numbers (skeleton, (const gchar * const *)own_numbers);
+ g_object_unref (skeleton);
+ }
+}
+
static void
load_own_numbers_ready (MMIfaceModem *self,
GAsyncResult *res,
@@ -4106,6 +4237,10 @@ interface_initialization_step (InitializationContext *ctx)
/* Create new default list */
list = mm_bearer_list_new (n, n);
+ g_signal_connect (list,
+ "notify::" MM_BEARER_LIST_NUM_BEARERS,
+ G_CALLBACK (bearer_list_updated),
+ ctx->self);
g_object_set (ctx->self,
MM_IFACE_MODEM_BEARER_LIST, list,
NULL);
@@ -4335,7 +4470,7 @@ interface_initialization_step (InitializationContext *ctx)
* This will try to load any missing property value that couldn't be
* retrieved before due to having the SIM locked. */
mm_sim_initialize (sim,
- NULL, /* TODO: cancellable */
+ ctx->cancellable,
(GAsyncReadyCallback)sim_reinit_ready,
ctx);
g_object_unref (sim);
@@ -4441,6 +4576,17 @@ interface_initialization_step (InitializationContext *ctx)
"handle-set-current-capabilities",
G_CALLBACK (handle_set_current_capabilities),
ctx->self);
+ /* Allow the reset and factory reset operation in FAILED state to rescue the modem.
+ * Also, for a modem that doesn't support SIM hot swapping, a reset is needed to
+ * force the modem to detect the newly inserted SIM. */
+ g_signal_connect (ctx->skeleton,
+ "handle-reset",
+ G_CALLBACK (handle_reset),
+ ctx->self);
+ g_signal_connect (ctx->skeleton,
+ "handle-factory-reset",
+ G_CALLBACK (handle_factory_reset),
+ ctx->self);
if (ctx->fatal_error) {
g_simple_async_result_take_error (ctx->result, ctx->fatal_error);
@@ -4473,14 +4619,6 @@ interface_initialization_step (InitializationContext *ctx)
G_CALLBACK (handle_set_power_state),
ctx->self);
g_signal_connect (ctx->skeleton,
- "handle-reset",
- G_CALLBACK (handle_reset),
- ctx->self);
- g_signal_connect (ctx->skeleton,
- "handle-factory-reset",
- G_CALLBACK (handle_factory_reset),
- ctx->self);
- g_signal_connect (ctx->skeleton,
"handle-set-current-bands",
G_CALLBACK (handle_set_current_bands),
ctx->self);
@@ -4806,6 +4944,26 @@ mm_iface_modem_is_cdma_only (MMIfaceModem *self)
/*****************************************************************************/
+const gchar *
+mm_iface_modem_get_model (MMIfaceModem *self)
+{
+ const gchar *model = NULL;
+ MmGdbusModem *skeleton;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
+ NULL);
+
+ if (skeleton) {
+ model = mm_gdbus_modem_get_model (skeleton);
+ g_object_unref (skeleton);
+ }
+
+ return model;
+}
+
+/*****************************************************************************/
+
static void
iface_modem_init (gpointer g_iface)
{
diff --git a/src/mm-iface-modem.h b/src/mm-iface-modem.h
index cadf31e..d8819c1 100644
--- a/src/mm-iface-modem.h
+++ b/src/mm-iface-modem.h
@@ -344,6 +344,9 @@ gboolean mm_iface_modem_is_3g_only (MMIfaceModem *self);
gboolean mm_iface_modem_is_4g (MMIfaceModem *self);
gboolean mm_iface_modem_is_4g_only (MMIfaceModem *self);
+/* Helper to query model */
+const gchar *mm_iface_modem_get_model (MMIfaceModem *self);
+
/* Initialize Modem interface (async) */
void mm_iface_modem_initialize (MMIfaceModem *self,
GCancellable *cancellable,
@@ -416,6 +419,10 @@ void mm_iface_modem_update_state (MMIfaceModem *self,
void mm_iface_modem_update_failed_state (MMIfaceModem *self,
MMModemStateFailedReason failed_reason);
+/* Allow update own numbers */
+void mm_iface_modem_update_own_numbers (MMIfaceModem *self,
+ const GStrv own_numbers);
+
/* Allow reporting new access tech */
void mm_iface_modem_update_access_technologies (MMIfaceModem *self,
MMModemAccessTechnology access_tech,
diff --git a/src/mm-marshal.list b/src/mm-marshal.list
deleted file mode 100644
index 62b6882..0000000
--- a/src/mm-marshal.list
+++ /dev/null
@@ -1,3 +0,0 @@
-VOID:STRING,BOOLEAN
-VOID:STRING
-VOID:BOOLEAN
diff --git a/src/mm-modem-helpers-mbim.c b/src/mm-modem-helpers-mbim.c
index d58ed59..8060ace 100644
--- a/src/mm-modem-helpers-mbim.c
+++ b/src/mm-modem-helpers-mbim.c
@@ -14,6 +14,7 @@
*/
#include "mm-modem-helpers-mbim.h"
+#include "mm-modem-helpers.h"
#include "mm-enums-types.h"
#include "mm-errors-types.h"
#include "mm-log.h"
@@ -127,6 +128,53 @@ mm_modem_access_technology_from_mbim_data_class (MbimDataClass data_class)
/*****************************************************************************/
+MMModem3gppNetworkAvailability
+mm_modem_3gpp_network_availability_from_mbim_provider_state (MbimProviderState state)
+{
+ switch (state) {
+ case MBIM_PROVIDER_STATE_HOME:
+ case MBIM_PROVIDER_STATE_PREFERRED:
+ case MBIM_PROVIDER_STATE_VISIBLE:
+ case MBIM_PROVIDER_STATE_PREFERRED_MULTICARRIER:
+ return MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE;
+ case MBIM_PROVIDER_STATE_REGISTERED:
+ return MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT;
+ case MBIM_PROVIDER_STATE_FORBIDDEN:
+ return MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN;
+ case MBIM_PROVIDER_STATE_UNKNOWN:
+ default:
+ return MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN;
+ }
+}
+
+/*****************************************************************************/
+
+GList *
+mm_3gpp_network_info_list_from_mbim_providers (const MbimProvider *const *providers, guint n_providers)
+{
+ GList *info_list = NULL;
+ guint i;
+
+ g_return_val_if_fail (providers != NULL, NULL);
+
+ for (i = 0; i < n_providers; i++) {
+ MM3gppNetworkInfo *info;
+
+ info = g_new0 (MM3gppNetworkInfo, 1);
+ info->status = mm_modem_3gpp_network_availability_from_mbim_provider_state (providers[i]->provider_state);
+ info->operator_long = g_strdup (providers[i]->provider_name);
+ info->operator_short = g_strdup (providers[i]->provider_name);
+ info->operator_code = g_strdup (providers[i]->provider_id);
+ info->access_tech = mm_modem_access_technology_from_mbim_data_class (providers[i]->cellular_class);
+
+ info_list = g_list_append (info_list, info);
+ }
+
+ return info_list;
+}
+
+/*****************************************************************************/
+
GError *
mm_mobile_equipment_error_from_mbim_nw_error (MbimNwError nw_error)
{
diff --git a/src/mm-modem-helpers-mbim.h b/src/mm-modem-helpers-mbim.h
index 28d8f7e..67b6b57 100644
--- a/src/mm-modem-helpers-mbim.h
+++ b/src/mm-modem-helpers-mbim.h
@@ -30,6 +30,10 @@ MMModem3gppRegistrationState mm_modem_3gpp_registration_state_from_mbim_register
MMModemAccessTechnology mm_modem_access_technology_from_mbim_data_class (MbimDataClass data_class);
+MMModem3gppNetworkAvailability mm_modem_3gpp_network_availability_from_mbim_provider_state (MbimProviderState state);
+
+GList *mm_3gpp_network_info_list_from_mbim_providers (const MbimProvider *const *providers, guint n_providers);
+
GError *mm_mobile_equipment_error_from_mbim_nw_error (MbimNwError nw_error);
/*****************************************************************************/
diff --git a/src/mm-modem-helpers-qmi.c b/src/mm-modem-helpers-qmi.c
index 61d8b27..4a818ec 100644
--- a/src/mm-modem-helpers-qmi.c
+++ b/src/mm-modem-helpers-qmi.c
@@ -1271,3 +1271,106 @@ mm_modem_capability_from_qmi_capabilities_context (MMQmiCapabilitiesContext *ctx
return tmp;
}
+
+/*****************************************************************************/
+
+MMOmaSessionType
+mm_oma_session_type_from_qmi_oma_session_type (QmiOmaSessionType qmi_session_type)
+{
+ switch (qmi_session_type) {
+ case QMI_OMA_SESSION_TYPE_CLIENT_INITIATED_DEVICE_CONFIGURE:
+ return MM_OMA_SESSION_TYPE_CLIENT_INITIATED_DEVICE_CONFIGURE;
+ case QMI_OMA_SESSION_TYPE_CLIENT_INITIATED_PRL_UPDATE:
+ return MM_OMA_SESSION_TYPE_CLIENT_INITIATED_PRL_UPDATE;
+ case QMI_OMA_SESSION_TYPE_CLIENT_INITIATED_HANDS_FREE_ACTIVATION:
+ return MM_OMA_SESSION_TYPE_CLIENT_INITIATED_HANDS_FREE_ACTIVATION;
+ case QMI_OMA_SESSION_TYPE_DEVICE_INITIATED_HANDS_FREE_ACTIVATION:
+ return MM_OMA_SESSION_TYPE_DEVICE_INITIATED_HANDS_FREE_ACTIVATION;
+ case QMI_OMA_SESSION_TYPE_NETWORK_INITIATED_PRL_UPDATE:
+ return MM_OMA_SESSION_TYPE_NETWORK_INITIATED_PRL_UPDATE;
+ case QMI_OMA_SESSION_TYPE_NETWORK_INITIATED_DEVICE_CONFIGURE:
+ return MM_OMA_SESSION_TYPE_NETWORK_INITIATED_DEVICE_CONFIGURE;
+ case QMI_OMA_SESSION_TYPE_DEVICE_INITIATED_PRL_UPDATE:
+ return MM_OMA_SESSION_TYPE_DEVICE_INITIATED_PRL_UPDATE;
+ default:
+ return MM_OMA_SESSION_TYPE_UNKNOWN;
+ }
+}
+
+QmiOmaSessionType
+mm_oma_session_type_to_qmi_oma_session_type (MMOmaSessionType mm_session_type)
+{
+ switch (mm_session_type) {
+ case MM_OMA_SESSION_TYPE_CLIENT_INITIATED_DEVICE_CONFIGURE:
+ return QMI_OMA_SESSION_TYPE_CLIENT_INITIATED_DEVICE_CONFIGURE;
+ case MM_OMA_SESSION_TYPE_CLIENT_INITIATED_PRL_UPDATE:
+ return QMI_OMA_SESSION_TYPE_CLIENT_INITIATED_PRL_UPDATE;
+ case MM_OMA_SESSION_TYPE_CLIENT_INITIATED_HANDS_FREE_ACTIVATION:
+ return QMI_OMA_SESSION_TYPE_CLIENT_INITIATED_HANDS_FREE_ACTIVATION;
+ case MM_OMA_SESSION_TYPE_DEVICE_INITIATED_HANDS_FREE_ACTIVATION:
+ return QMI_OMA_SESSION_TYPE_DEVICE_INITIATED_HANDS_FREE_ACTIVATION;
+ case MM_OMA_SESSION_TYPE_NETWORK_INITIATED_PRL_UPDATE:
+ return QMI_OMA_SESSION_TYPE_NETWORK_INITIATED_PRL_UPDATE;
+ case MM_OMA_SESSION_TYPE_NETWORK_INITIATED_DEVICE_CONFIGURE:
+ return QMI_OMA_SESSION_TYPE_NETWORK_INITIATED_DEVICE_CONFIGURE;
+ case MM_OMA_SESSION_TYPE_DEVICE_INITIATED_PRL_UPDATE:
+ return QMI_OMA_SESSION_TYPE_DEVICE_INITIATED_PRL_UPDATE;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+MMOmaSessionState
+mm_oma_session_state_from_qmi_oma_session_state (QmiOmaSessionState qmi_session_state)
+{
+ /* Note: MM_OMA_SESSION_STATE_STARTED is not a state received from the modem */
+
+ switch (qmi_session_state) {
+ case QMI_OMA_SESSION_STATE_COMPLETE_INFORMATION_UPDATED:
+ case QMI_OMA_SESSION_STATE_COMPLETE_UPDATED_INFORMATION_UNAVAILABLE:
+ return MM_OMA_SESSION_STATE_COMPLETED;
+ case QMI_OMA_SESSION_STATE_FAILED:
+ return MM_OMA_SESSION_STATE_FAILED;
+ case QMI_OMA_SESSION_STATE_RETRYING:
+ return MM_OMA_SESSION_STATE_RETRYING;
+ case QMI_OMA_SESSION_STATE_CONNECTING:
+ return MM_OMA_SESSION_STATE_CONNECTING;
+ case QMI_OMA_SESSION_STATE_CONNECTED:
+ return MM_OMA_SESSION_STATE_CONNECTED;
+ case QMI_OMA_SESSION_STATE_AUTHENTICATED:
+ return MM_OMA_SESSION_STATE_AUTHENTICATED;
+ case QMI_OMA_SESSION_STATE_MDN_DOWNLOADED:
+ return MM_OMA_SESSION_STATE_MDN_DOWNLOADED;
+ case QMI_OMA_SESSION_STATE_MSID_DOWNLOADED:
+ return MM_OMA_SESSION_STATE_MSID_DOWNLOADED;
+ case QMI_OMA_SESSION_STATE_PRL_DOWNLOADED:
+ return MM_OMA_SESSION_STATE_PRL_DOWNLOADED;
+ case QMI_OMA_SESSION_STATE_MIP_PROFILE_DOWNLOADED:
+ return MM_OMA_SESSION_STATE_MIP_PROFILE_DOWNLOADED;
+ default:
+ return MM_OMA_SESSION_STATE_UNKNOWN;
+ }
+}
+
+/*****************************************************************************/
+
+MMOmaSessionStateFailedReason
+mm_oma_session_state_failed_reason_from_qmi_oma_session_failed_reason (QmiOmaSessionFailedReason qmi_session_failed_reason)
+{
+ switch (qmi_session_failed_reason) {
+ case QMI_OMA_SESSION_FAILED_REASON_UNKNOWN:
+ return MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN;
+ case QMI_OMA_SESSION_FAILED_REASON_NETWORK_UNAVAILABLE:
+ return MM_OMA_SESSION_STATE_FAILED_REASON_NETWORK_UNAVAILABLE;
+ case QMI_OMA_SESSION_FAILED_REASON_SERVER_UNAVAILABLE:
+ return MM_OMA_SESSION_STATE_FAILED_REASON_SERVER_UNAVAILABLE;
+ case QMI_OMA_SESSION_FAILED_REASON_AUTHENTICATION_FAILED:
+ return MM_OMA_SESSION_STATE_FAILED_REASON_AUTHENTICATION_FAILED;
+ case QMI_OMA_SESSION_FAILED_REASON_MAX_RETRY_EXCEEDED:
+ return MM_OMA_SESSION_STATE_FAILED_REASON_MAX_RETRY_EXCEEDED;
+ case QMI_OMA_SESSION_FAILED_REASON_SESSION_CANCELLED:
+ return MM_OMA_SESSION_STATE_FAILED_REASON_SESSION_CANCELLED;
+ default:
+ return MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN;
+ }
+}
diff --git a/src/mm-modem-helpers-qmi.h b/src/mm-modem-helpers-qmi.h
index 63b0531..fb3ef8d 100644
--- a/src/mm-modem-helpers-qmi.h
+++ b/src/mm-modem-helpers-qmi.h
@@ -95,6 +95,16 @@ MMSmsState mm_sms_state_from_qmi_message_tag (QmiWmsMessageTagType tag);
QmiWdsAuthentication mm_bearer_allowed_auth_to_qmi_authentication (MMBearerAllowedAuth auth);
/*****************************************************************************/
+/* QMI/OMA to MM translations */
+
+MMOmaSessionType mm_oma_session_type_from_qmi_oma_session_type (QmiOmaSessionType qmi_session_type);
+QmiOmaSessionType mm_oma_session_type_to_qmi_oma_session_type (MMOmaSessionType mm_session_type);
+
+MMOmaSessionState mm_oma_session_state_from_qmi_oma_session_state (QmiOmaSessionState qmi_session_state);
+
+MMOmaSessionStateFailedReason mm_oma_session_state_failed_reason_from_qmi_oma_session_failed_reason (QmiOmaSessionFailedReason qmi_session_failed_reason);
+
+/*****************************************************************************/
/* Utility to gather current capabilities from various sources */
typedef struct {
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index cd4ef76..9eefb33 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -333,20 +333,22 @@ mm_filter_supported_capabilities (MMModemCapability all,
#define CREG3 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)"
/* +CREG: <n>,<stat>,<lac>,<ci> (GSM 07.07 solicited and some CREG=2 unsolicited) */
-#define CREG4 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*([^,]*)\\s*,\\s*([^,\\s]*)"
+#define CREG4 "\\+(CREG|CGREG|CEREG):\\s*([0-9]),\\s*([0-9])\\s*,\\s*([^,]*)\\s*,\\s*([^,\\s]*)"
+#define CREG5 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*(\"[^,]*\")\\s*,\\s*(\"[^,\\s]*\")"
/* +CREG: <stat>,<lac>,<ci>,<AcT> (ETSI 27.007 CREG=2 unsolicited) */
-#define CREG5 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])"
+#define CREG6 "\\+(CREG|CGREG|CEREG):\\s*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([0-9])"
+#define CREG7 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9])\\s*,\\s*(\"[^,\\s]*\")\\s*,\\s*(\"[^,\\s]*\")\\s*,\\s*0*([0-9])"
/* +CREG: <n>,<stat>,<lac>,<ci>,<AcT> (ETSI 27.007 solicited and some CREG=2 unsolicited) */
-#define CREG6 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])"
+#define CREG8 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])"
/* +CREG: <n>,<stat>,<lac>,<ci>,<AcT?>,<something> (Samsung Wave S8500) */
/* '<CR><LF>+CREG: 2,1,000B,2816, B, C2816<CR><LF><CR><LF>OK<CR><LF>' */
-#define CREG7 "\\+(CREG|CGREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*[^,\\s]*"
+#define CREG9 "\\+(CREG|CGREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*[^,\\s]*"
/* +CREG: <stat>,<lac>,<ci>,<AcT>,<RAC> (ETSI 27.007 v9.20 CREG=2 unsolicited with RAC) */
-#define CREG8 "\\+(CREG|CGREG):\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])\\s*,\\s*([^,\\s]*)"
+#define CREG10 "\\+(CREG|CGREG):\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])\\s*,\\s*([^,\\s]*)"
/* +CEREG: <stat>,<lac>,<rac>,<ci>,<AcT> (ETSI 27.007 v8.6 CREG=2 unsolicited with RAC) */
#define CEREG1 "\\+(CEREG):\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])"
@@ -357,7 +359,7 @@ mm_filter_supported_capabilities (MMModemCapability all,
GPtrArray *
mm_3gpp_creg_regex_get (gboolean solicited)
{
- GPtrArray *array = g_ptr_array_sized_new (10);
+ GPtrArray *array = g_ptr_array_sized_new (12);
GRegex *regex;
/* #1 */
@@ -424,6 +426,22 @@ mm_3gpp_creg_regex_get (gboolean solicited)
g_assert (regex);
g_ptr_array_add (array, regex);
+ /* #9 */
+ if (solicited)
+ regex = g_regex_new (CREG9 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG9 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ /* #10 */
+ if (solicited)
+ regex = g_regex_new (CREG10 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG10 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
/* CEREG #1 */
if (solicited)
regex = g_regex_new (CEREG1 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
@@ -746,7 +764,7 @@ mm_3gpp_parse_cgdcont_test_response (const gchar *response,
return NULL;
}
- r = g_regex_new ("\\+CGDCONT:\\s*\\((\\d+)-(\\d+)\\),\\(?\"(\\S+)\"",
+ r = g_regex_new ("\\+CGDCONT:\\s*\\((\\d+)-?(\\d+)?\\),\\(?\"(\\S+)\"",
G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
0, &inner_error);
g_assert (r != NULL);
@@ -768,19 +786,18 @@ mm_3gpp_parse_cgdcont_test_response (const gchar *response,
if (!mm_get_uint_from_match_info (match_info, 1, &min_cid))
mm_warn ("Invalid min CID in CGDCONT=? reply for PDP type '%s'", pdp_type_str);
else {
- /* Read max CID */
+ MM3gppPdpContextFormat *format;
+
+ /* Read max CID: Optional! If no value given, we default to min CID */
if (!mm_get_uint_from_match_info (match_info, 2, &max_cid))
- mm_warn ("Invalid max CID in CGDCONT=? reply for PDP type '%s'", pdp_type_str);
- else {
- MM3gppPdpContextFormat *format;
+ max_cid = min_cid;
- format = g_slice_new (MM3gppPdpContextFormat);
- format->pdp_type = pdp_type;
- format->min_cid = min_cid;
- format->max_cid = max_cid;
+ format = g_slice_new (MM3gppPdpContextFormat);
+ format->pdp_type = pdp_type;
+ format->min_cid = min_cid;
+ format->max_cid = max_cid;
- list = g_list_prepend (list, format);
- }
+ list = g_list_prepend (list, format);
}
}
@@ -1760,7 +1777,7 @@ mm_string_to_access_tech (const gchar *string)
if (strcasestr (string, "HSDPA"))
act |= MM_MODEM_ACCESS_TECHNOLOGY_HSDPA;
- if (strcasestr (string, "UMTS"))
+ if (strcasestr (string, "UMTS") || strcasestr (string, "3G"))
act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
if (strcasestr (string, "EDGE"))
@@ -1873,6 +1890,105 @@ mm_3gpp_get_ip_family_from_pdp_type (const gchar *pdp_type)
/*************************************************************************/
+char *
+mm_3gpp_parse_iccid (const char *raw_iccid, GError **error)
+{
+ gboolean swap;
+ char *buf, *swapped = NULL;
+ gsize len = 0;
+ int f_pos = -1, i;
+
+ g_return_val_if_fail (raw_iccid != NULL, NULL);
+
+ /* Skip spaces and quotes */
+ while (raw_iccid && *raw_iccid && (isspace (*raw_iccid) || *raw_iccid == '"'))
+ raw_iccid++;
+
+ /* Make sure the buffer is only digits or 'F' */
+ buf = g_strdup (raw_iccid);
+ for (len = 0; buf[len]; len++) {
+ if (isdigit (buf[len]))
+ continue;
+ if (buf[len] == 'F' || buf[len] == 'f') {
+ buf[len] = 'F'; /* canonicalize the F */
+ f_pos = len;
+ continue;
+ }
+ if (buf[len] == '\"') {
+ buf[len] = 0;
+ break;
+ }
+
+ /* Invalid character */
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "ICCID response contained invalid character '%c'",
+ buf[len]);
+ goto error;
+ }
+
+ /* BCD encoded ICCIDs are 20 digits long */
+ if (len != 20) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Invalid ICCID response size (was %zd, expected 20)",
+ len);
+ goto error;
+ }
+
+ /* The leading two digits of an ICCID is the major industry identifier and
+ * should be '89' for telecommunication purposes according to ISO/IEC 7812.
+ */
+ if (buf[0] == '8' && buf[1] == '9') {
+ swap = FALSE;
+ } else if (buf[0] == '9' && buf[1] == '8') {
+ swap = TRUE;
+ } else {
+ /* FIXME: Instead of erroring out, revisit this solution if we find any SIM
+ * that doesn't use '89' as the major industry identifier of the ICCID.
+ */
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Invalid ICCID response (leading two digits are not 89)");
+ goto error;
+ }
+
+ /* Ensure if there's an 'F' that it's second-to-last if swap = TRUE,
+ * otherwise last if swap = FALSE */
+ if (f_pos >= 0) {
+ if ((swap && (f_pos != len - 2)) || (!swap && (f_pos != len - 1))) {
+ g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Invalid ICCID length (unexpected F position)");
+ goto error;
+ }
+ }
+
+ if (swap) {
+ /* Swap digits in the ICCID response to get the actual ICCID, each
+ * group of 2 digits is reversed.
+ *
+ * 21436587 -> 12345678
+ */
+ swapped = g_malloc0 (25);
+ for (i = 0; i < 10; i++) {
+ swapped[i * 2] = buf[(i * 2) + 1];
+ swapped[(i * 2) + 1] = buf[i * 2];
+ }
+ } else
+ swapped = g_strdup (buf);
+
+ /* Zero out the F for 19 digit ICCIDs */
+ if (swapped[len - 1] == 'F')
+ swapped[len - 1] = 0;
+
+ g_free (buf);
+ return swapped;
+
+error:
+ g_free (buf);
+ g_free (swapped);
+ return NULL;
+}
+
+/*************************************************************************/
+
gboolean
mm_3gpp_parse_operator_id (const gchar *operator_id,
guint16 *mcc,
diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h
index a1f1232..0ec59af 100644
--- a/src/mm-modem-helpers.h
+++ b/src/mm-modem-helpers.h
@@ -204,6 +204,8 @@ gboolean mm_3gpp_parse_operator_id (const gchar *operator_id,
const gchar *mm_3gpp_get_pdp_type_from_ip_family (MMBearerIpFamily family);
MMBearerIpFamily mm_3gpp_get_ip_family_from_pdp_type (const gchar *pdp_type);
+char *mm_3gpp_parse_iccid (const char *raw_iccid, GError **error);
+
/*****************************************************************************/
/* CDMA specific helpers and utilities */
/*****************************************************************************/
diff --git a/src/mm-plugin.c b/src/mm-plugin.c
index 9c28005..1d8bc83 100644
--- a/src/mm-plugin.c
+++ b/src/mm-plugin.c
@@ -33,7 +33,6 @@
#include "mm-at-serial-port.h"
#include "mm-qcdm-serial-port.h"
#include "mm-serial-parsers.h"
-#include "mm-marshal.h"
#include "mm-private-boxed-types.h"
#include "mm-log.h"
#include "mm-daemon-enums-types.h"
@@ -311,12 +310,17 @@ apply_pre_probing_filters (MMPlugin *self,
}
}
- /* If we got filtered by vendor or product IDs and we do not have vendor
- * or product strings to compare with: unsupported */
+ /* If we got filtered by vendor or product IDs; mark it as unsupported only if:
+ * a) we do not have vendor or product strings to compare with (i.e. plugin
+ * doesn't have explicit vendor/product strings
+ * b) the port is NOT an AT port which we can use for AT probing
+ */
if ((vendor_filtered || product_filtered) &&
- !self->priv->vendor_strings &&
- !self->priv->product_strings &&
- !self->priv->forbidden_product_strings) {
+ ((!self->priv->vendor_strings &&
+ !self->priv->product_strings &&
+ !self->priv->forbidden_product_strings) ||
+ g_str_equal (g_udev_device_get_subsystem (port), "net") ||
+ g_str_has_prefix (g_udev_device_get_name (port), "cdc-wdm"))) {
mm_dbg ("(%s) [%s] filtered by vendor/product IDs",
self->priv->name,
g_udev_device_get_name (port));
diff --git a/src/mm-port-probe.c b/src/mm-port-probe.c
index 8fb5706..4c64f2a 100644
--- a/src/mm-port-probe.c
+++ b/src/mm-port-probe.c
@@ -328,8 +328,10 @@ port_probe_run_task_free (PortProbeRunTask *task)
g_source_remove (task->source_id);
if (task->serial) {
- if (task->buffer_full_id)
+ if (task->buffer_full_id) {
+ g_warn_if_fail (MM_IS_AT_SERIAL_PORT (task->serial));
g_signal_handler_disconnect (task->serial, task->buffer_full_id);
+ }
if (mm_serial_port_is_open (task->serial))
mm_serial_port_close (task->serial);
g_object_unref (task->serial);
@@ -367,8 +369,8 @@ port_probe_run_task_complete (PortProbeRunTask *task,
{
/* As soon as we have the task completed, disable the buffer-full signal
* handling, so that we do not get unwanted errors reported */
- if (task->buffer_full_id) {
- g_source_remove (task->buffer_full_id);
+ if (task->serial && task->buffer_full_id) {
+ g_signal_handler_disconnect (task->serial, task->buffer_full_id);
task->buffer_full_id = 0;
}
@@ -658,6 +660,11 @@ serial_probe_qcdm (MMPortProbe *self)
/* If open, close the AT port */
if (task->serial) {
+ /* Explicitly clear the buffer full signal handler */
+ if (task->buffer_full_id) {
+ g_signal_handler_disconnect (task->serial, task->buffer_full_id);
+ task->buffer_full_id = 0;
+ }
mm_serial_port_close (task->serial);
g_object_unref (task->serial);
}
@@ -1146,7 +1153,6 @@ serial_open_at (MMPortProbe *self)
MM_SERIAL_PORT_SEND_DELAY, task->at_send_delay,
MM_AT_SERIAL_PORT_REMOVE_ECHO, task->at_remove_echo,
MM_AT_SERIAL_PORT_SEND_LF, task->at_send_lf,
- MM_PORT_CARRIER_DETECT, FALSE,
MM_SERIAL_PORT_SPEW_CONTROL, TRUE,
NULL);
diff --git a/src/mm-port.c b/src/mm-port.c
index 0d7ce75..6db4cc3 100644
--- a/src/mm-port.c
+++ b/src/mm-port.c
@@ -28,7 +28,6 @@ enum {
PROP_DEVICE,
PROP_SUBSYS,
PROP_TYPE,
- PROP_CARRIER_DETECT,
PROP_CONNECTED,
LAST_PROP
@@ -40,7 +39,6 @@ typedef struct {
char *device;
MMPortSubsys subsys;
MMPortType ptype;
- gboolean carrier_detect;
gboolean connected;
} MMPortPrivate;
@@ -114,15 +112,6 @@ mm_port_get_port_type (MMPort *self)
}
gboolean
-mm_port_get_carrier_detect (MMPort *self)
-{
- g_return_val_if_fail (self != NULL, MM_PORT_TYPE_UNKNOWN);
- g_return_val_if_fail (MM_IS_PORT (self), MM_PORT_TYPE_UNKNOWN);
-
- return MM_PORT_GET_PRIVATE (self)->carrier_detect;
-}
-
-gboolean
mm_port_get_connected (MMPort *self)
{
g_return_val_if_fail (self != NULL, FALSE);
@@ -176,9 +165,6 @@ set_property (GObject *object, guint prop_id,
/* Construct only */
priv->ptype = g_value_get_uint (value);
break;
- case PROP_CARRIER_DETECT:
- priv->carrier_detect = g_value_get_boolean (value);
- break;
case PROP_CONNECTED:
priv->connected = g_value_get_boolean (value);
break;
@@ -204,9 +190,6 @@ get_property (GObject *object, guint prop_id,
case PROP_TYPE:
g_value_set_uint (value, priv->ptype);
break;
- case PROP_CARRIER_DETECT:
- g_value_set_boolean (value, priv->carrier_detect);
- break;
case PROP_CONNECTED:
g_value_set_boolean (value, priv->connected);
break;
@@ -268,14 +251,6 @@ mm_port_class_init (MMPortClass *klass)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
- (object_class, PROP_CARRIER_DETECT,
- g_param_spec_boolean (MM_PORT_CARRIER_DETECT,
- "Carrier Detect",
- "Has Carrier Detect",
- TRUE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
- g_object_class_install_property
(object_class, PROP_CONNECTED,
g_param_spec_boolean (MM_PORT_CONNECTED,
"Connected",
diff --git a/src/mm-port.h b/src/mm-port.h
index e5395b4..6fc38a9 100644
--- a/src/mm-port.h
+++ b/src/mm-port.h
@@ -48,11 +48,10 @@ typedef enum { /*< underscore_name=mm_port_type >*/
#define MM_IS_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PORT))
#define MM_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PORT, MMPortClass))
-#define MM_PORT_DEVICE "device"
-#define MM_PORT_SUBSYS "subsys"
-#define MM_PORT_TYPE "type"
-#define MM_PORT_CARRIER_DETECT "carrier-detect"
-#define MM_PORT_CONNECTED "connected"
+#define MM_PORT_DEVICE "device"
+#define MM_PORT_SUBSYS "subsys"
+#define MM_PORT_TYPE "type"
+#define MM_PORT_CONNECTED "connected"
typedef struct _MMPort MMPort;
typedef struct _MMPortClass MMPortClass;
@@ -73,8 +72,6 @@ MMPortSubsys mm_port_get_subsys (MMPort *self);
MMPortType mm_port_get_port_type (MMPort *self);
-gboolean mm_port_get_carrier_detect (MMPort *self);
-
gboolean mm_port_get_connected (MMPort *self);
void mm_port_set_connected (MMPort *self, gboolean connected);
diff --git a/src/mm-qmi-port.c b/src/mm-qmi-port.c
index a26fdab..02ef09f 100644
--- a/src/mm-qmi-port.c
+++ b/src/mm-qmi-port.c
@@ -216,6 +216,12 @@ qmi_device_new_ready (GObject *unused,
GError *error = NULL;
QmiDeviceOpenFlags flags = QMI_DEVICE_OPEN_FLAGS_VERSION_INFO;
+ /* If possible, try to open the QMI port through the QMI proxy daemon, which
+ * allows other applications to also talk to the QMI port properly. */
+#if QMI_CHECK_VERSION (1,7,0)
+ flags |= QMI_DEVICE_OPEN_FLAGS_PROXY;
+#endif
+
ctx->self->priv->qmi_device = qmi_device_new_finish (res, &error);
if (!ctx->self->priv->qmi_device) {
g_simple_async_result_take_error (ctx->result, error);
diff --git a/src/mm-serial-port.c b/src/mm-serial-port.c
index 0f81c7b..cc4fc46 100644
--- a/src/mm-serial-port.c
+++ b/src/mm-serial-port.c
@@ -868,12 +868,22 @@ mm_serial_port_open (MMSerialPort *self, GError **error)
const char *device;
struct serial_struct sinfo = { 0 };
GTimeVal tv_start, tv_end;
+ int errno_save = 0;
g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE);
priv = MM_SERIAL_PORT_GET_PRIVATE (self);
device = mm_port_get_device (MM_PORT (self));
+ if (priv->forced_close) {
+ g_set_error (error,
+ MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_OPEN_FAILED,
+ "Could not open serial device %s: it has been forced close",
+ device);
+ return FALSE;
+ }
+
if (priv->reopen_id) {
g_set_error (error,
MM_SERIAL_ERROR,
@@ -897,6 +907,7 @@ mm_serial_port_open (MMSerialPort *self, GError **error)
devfile = g_strdup_printf ("/dev/%s", device);
errno = 0;
priv->fd = open (devfile, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY);
+ errno_save = errno;
g_free (devfile);
}
@@ -908,13 +919,16 @@ mm_serial_port_open (MMSerialPort *self, GError **error)
g_set_error (error,
MM_SERIAL_ERROR,
(errno == ENODEV) ? MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE : MM_SERIAL_ERROR_OPEN_FAILED,
- "Could not open serial device %s: %s", device, strerror (errno));
+ "Could not open serial device %s: %s", device, strerror (errno_save));
+ mm_warn ("(%s) could not open serial device (%d)", device, errno_save);
return FALSE;
}
if (ioctl (priv->fd, TIOCEXCL) < 0) {
+ errno_save = errno;
g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED,
- "Could not lock serial device %s: %s", device, strerror (errno));
+ "Could not lock serial device %s: %s", device, strerror (errno_save));
+ mm_warn ("(%s) could not lock serial device (%d)", device, errno_save);
goto error;
}
@@ -922,14 +936,18 @@ mm_serial_port_open (MMSerialPort *self, GError **error)
tcflush (priv->fd, TCIOFLUSH);
if (tcgetattr (priv->fd, &priv->old_t) < 0) {
+ errno_save = errno;
g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED,
- "Could not open serial device %s: %s", device, strerror (errno));
+ "Could not set attributes on serial device %s: %s", device, strerror (errno_save));
+ mm_warn ("(%s) could not set attributes on serial device (%d)", device, errno_save);
goto error;
}
g_warn_if_fail (MM_SERIAL_PORT_GET_CLASS (self)->config_fd);
- if (!MM_SERIAL_PORT_GET_CLASS (self)->config_fd (self, priv->fd, error))
+ if (!MM_SERIAL_PORT_GET_CLASS (self)->config_fd (self, priv->fd, error)) {
+ mm_dbg ("(%s) failed to configure serial device", device);
goto error;
+ }
/* Don't wait for pending data when closing the port; this can cause some
* stupid devices that don't respond to URBs on a particular port to hang
@@ -966,6 +984,7 @@ success:
return TRUE;
error:
+ mm_warn ("(%s) failed to open serial device", device);
close (priv->fd);
priv->fd = -1;
return FALSE;
@@ -1102,6 +1121,13 @@ mm_serial_port_close (MMSerialPort *self)
priv->queue_id = 0;
}
+ if (priv->cancellable_id) {
+ g_assert (priv->cancellable != NULL);
+ g_cancellable_disconnect (priv->cancellable,
+ priv->cancellable_id);
+ priv->cancellable_id = 0;
+ }
+
g_clear_object (&priv->cancellable);
}
@@ -1276,6 +1302,17 @@ mm_serial_port_reopen (MMSerialPort *self,
g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE);
priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ if (priv->forced_close) {
+ GError *error;
+
+ error = g_error_new_literal (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Serial port has been forced close.");
+ callback (self, error, user_data);
+ g_error_free (error);
+ return FALSE;
+ }
+
if (priv->reopen_id > 0) {
GError *error;
@@ -1711,7 +1748,7 @@ mm_serial_port_class_init (MMSerialPortClass *klass)
(object_class, PROP_FD,
g_param_spec_int (MM_SERIAL_PORT_FD,
"File descriptor",
- "Fiel descriptor",
+ "File descriptor",
-1, G_MAXINT, -1,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
@@ -1783,12 +1820,12 @@ mm_serial_port_class_init (MMSerialPortClass *klass)
/* Signals */
signals[BUFFER_FULL] =
g_signal_new ("buffer-full",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (MMSerialPortClass, buffer_full),
- NULL, NULL,
- g_cclosure_marshal_VOID__POINTER,
- G_TYPE_NONE, 1, G_TYPE_POINTER);
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMSerialPortClass, buffer_full),
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
signals[TIMED_OUT] =
g_signal_new ("timed-out",
@@ -1796,15 +1833,15 @@ mm_serial_port_class_init (MMSerialPortClass *klass)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (MMSerialPortClass, timed_out),
NULL, NULL,
- g_cclosure_marshal_VOID__UINT,
+ g_cclosure_marshal_generic,
G_TYPE_NONE, 1, G_TYPE_UINT);
signals[FORCED_CLOSE] =
g_signal_new ("forced-close",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (MMSerialPortClass, forced_close),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMSerialPortClass, forced_close),
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 0);
}
diff --git a/src/mm-sim-mbim.c b/src/mm-sim-mbim.c
index 66d908a..62bd082 100644
--- a/src/mm-sim-mbim.c
+++ b/src/mm-sim-mbim.c
@@ -73,7 +73,7 @@ load_sim_identifier_finish (MMSim *self,
{
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
return NULL;
- return (gchar *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ return g_strdup ((gchar *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
}
static void
@@ -97,7 +97,7 @@ simid_subscriber_ready_state_ready (MbimDevice *device,
NULL, /* telephone_numbers_count */
NULL, /* telephone_numbers */
&error))
- g_simple_async_result_set_op_res_gpointer (simple, sim_iccid, NULL);
+ g_simple_async_result_set_op_res_gpointer (simple, sim_iccid, (GDestroyNotify)g_free);
else
g_simple_async_result_take_error (simple, error);
@@ -141,7 +141,7 @@ load_imsi_finish (MMSim *self,
{
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
return NULL;
- return (gchar *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ return g_strdup ((gchar *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
}
static void
@@ -165,7 +165,7 @@ imsi_subscriber_ready_state_ready (MbimDevice *device,
NULL, /* telephone_numbers_count */
NULL, /* telephone_numbers */
&error))
- g_simple_async_result_set_op_res_gpointer (simple, subscriber_id, NULL);
+ g_simple_async_result_set_op_res_gpointer (simple, subscriber_id, (GDestroyNotify)g_free);
else
g_simple_async_result_take_error (simple, error);
@@ -200,6 +200,140 @@ load_imsi (MMSim *self,
}
/*****************************************************************************/
+/* Load operator identifier */
+
+static gchar *
+load_operator_identifier_finish (MMSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ MbimProvider *provider;
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return NULL;
+
+ provider = (MbimProvider *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ return g_strdup (provider->provider_id);
+}
+
+static void
+load_operator_identifier_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GSimpleAsyncResult *simple)
+{
+ MbimMessage *response;
+ GError *error = NULL;
+ MbimProvider *provider;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (response &&
+ mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_home_provider_response_parse (
+ response,
+ &provider,
+ &error))
+ g_simple_async_result_set_op_res_gpointer (simple, provider, (GDestroyNotify)mbim_provider_free);
+ else
+ g_simple_async_result_take_error (simple, error);
+
+ if (response)
+ mbim_message_unref (response);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+load_operator_identifier (MMSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MbimDevice *device;
+ MbimMessage *message;
+ GSimpleAsyncResult *result;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, load_operator_identifier);
+
+ message = mbim_message_home_provider_query_new (NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)load_operator_identifier_ready,
+ result);
+ mbim_message_unref (message);
+}
+
+/*****************************************************************************/
+/* Load operator name */
+
+static gchar *
+load_operator_name_finish (MMSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ MbimProvider *provider;
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return NULL;
+
+ provider = (MbimProvider *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ return g_strdup (provider->provider_name);
+}
+
+static void
+load_operator_name_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GSimpleAsyncResult *simple)
+{
+ MbimMessage *response;
+ GError *error = NULL;
+ MbimProvider *provider;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (response &&
+ mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_home_provider_response_parse (
+ response,
+ &provider,
+ &error))
+ g_simple_async_result_set_op_res_gpointer (simple, provider, (GDestroyNotify)mbim_provider_free);
+ else
+ g_simple_async_result_take_error (simple, error);
+
+ if (response)
+ mbim_message_unref (response);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+}
+
+static void
+load_operator_name (MMSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MbimDevice *device;
+ MbimMessage *message;
+ GSimpleAsyncResult *result;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, load_operator_name);
+
+ message = mbim_message_home_provider_query_new (NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)load_operator_name_ready,
+ result);
+ mbim_message_unref (message);
+}
+
+/*****************************************************************************/
/* Send PIN */
static gboolean
@@ -594,6 +728,10 @@ mm_sim_mbim_class_init (MMSimMbimClass *klass)
sim_class->load_sim_identifier_finish = load_sim_identifier_finish;
sim_class->load_imsi = load_imsi;
sim_class->load_imsi_finish = load_imsi_finish;
+ sim_class->load_operator_identifier = load_operator_identifier;
+ sim_class->load_operator_identifier_finish = load_operator_identifier_finish;
+ sim_class->load_operator_name = load_operator_name;
+ sim_class->load_operator_name_finish = load_operator_name_finish;
sim_class->send_pin = send_pin;
sim_class->send_pin_finish = send_pin_finish;
sim_class->send_puk = send_puk;
@@ -602,10 +740,4 @@ mm_sim_mbim_class_init (MMSimMbimClass *klass)
sim_class->enable_pin_finish = enable_pin_finish;
sim_class->change_pin = change_pin;
sim_class->change_pin_finish = change_pin_finish;
-
- /* TODO: use MBIM_CID_HOME_PROVIDER */
- sim_class->load_operator_identifier = NULL;
- sim_class->load_operator_identifier_finish = NULL;
- sim_class->load_operator_name = NULL;
- sim_class->load_operator_name_finish = NULL;
}
diff --git a/src/mm-sim.c b/src/mm-sim.c
index 3e1ca46..7cd84ff 100644
--- a/src/mm-sim.c
+++ b/src/mm-sim.c
@@ -32,7 +32,6 @@
#include "mm-base-modem.h"
#include "mm-log.h"
#include "mm-modem-helpers.h"
-#include "mm-marshal.h"
static void async_initable_iface_init (GAsyncInitableIface *iface);
@@ -946,7 +945,6 @@ parse_iccid (const gchar *response,
GError **error)
{
gchar buf[21];
- gchar swapped[21];
const gchar *str;
gint sw1;
gint sw2;
@@ -974,69 +972,7 @@ parse_iccid (const gchar *response,
(sw1 == 0x91) ||
(sw1 == 0x92) ||
(sw1 == 0x9f)) {
- gsize len = 0;
- gint f_pos = -1;
- gint i;
-
- /* Make sure the buffer is only digits or 'F' */
- for (len = 0; len < sizeof (buf) && buf[len]; len++) {
- if (isdigit (buf[len]))
- continue;
- if (buf[len] == 'F' || buf[len] == 'f') {
- buf[len] = 'F'; /* canonicalize the F */
- f_pos = len;
- continue;
- }
- if (buf[len] == '\"') {
- buf[len] = 0;
- break;
- }
-
- /* Invalid character */
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "CRSM ICCID response contained invalid character '%c'",
- buf[len]);
- return NULL;
- }
-
- /* BCD encoded ICCIDs are 20 digits long */
- if (len != 20) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Invalid +CRSM ICCID response size (was %zd, expected 20)",
- len);
- return NULL;
- }
-
- /* Ensure if there's an 'F' that it's second-to-last */
- if ((f_pos >= 0) && (f_pos != len - 2)) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Invalid +CRSM ICCID length (unexpected F)");
- return NULL;
- }
-
- /* Swap digits in the EFiccid response to get the actual ICCID, each
- * group of 2 digits is reversed in the +CRSM response. i.e.:
- *
- * 21436587 -> 12345678
- */
- memset (swapped, 0, sizeof (swapped));
- for (i = 0; i < 10; i++) {
- swapped[i * 2] = buf[(i * 2) + 1];
- swapped[(i * 2) + 1] = buf[i * 2];
- }
-
- /* Zero out the F for 19 digit ICCIDs */
- if (swapped[len - 1] == 'F')
- swapped[len - 1] = 0;
-
-
- return g_strdup (swapped);
+ return mm_3gpp_parse_iccid (buf, error);
} else {
g_set_error (error,
MM_CORE_ERROR,
@@ -1532,6 +1468,16 @@ STR_REPLY_READY_FN (operator_name, "Operator name")
static void
interface_initialization_step (InitAsyncContext *ctx)
{
+ if (g_cancellable_is_cancelled (ctx->cancellable)) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_CANCELLED,
+ "Interface initialization cancelled");
+ g_simple_async_result_complete_in_idle (ctx->result);
+ init_async_context_free (ctx);
+ return;
+ }
+
switch (ctx->step) {
case INITIALIZATION_STEP_FIRST:
/* Fall down to next step */
@@ -1860,6 +1806,6 @@ mm_sim_class_init (MMSimClass *klass)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (MMSimClass, pin_lock_enabled),
NULL, NULL,
- mm_marshal_VOID__BOOLEAN,
+ g_cclosure_marshal_generic,
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
}
diff --git a/src/mm-sms-list.c b/src/mm-sms-list.c
index 258470e..79fb64c 100644
--- a/src/mm-sms-list.c
+++ b/src/mm-sms-list.c
@@ -25,7 +25,6 @@
#include <libmm-glib.h>
#include "mm-iface-modem-messaging.h"
-#include "mm-marshal.h"
#include "mm-sms-list.h"
#include "mm-sms.h"
#include "mm-log.h"
@@ -226,6 +225,9 @@ mm_sms_list_add_sms (MMSmsList *self,
MMSms *sms)
{
self->priv->list = g_list_prepend (self->priv->list, g_object_ref (sms));
+ g_signal_emit (self, signals[SIGNAL_ADDED], 0,
+ mm_sms_get_path (sms),
+ FALSE);
}
/*****************************************************************************/
@@ -481,7 +483,7 @@ mm_sms_list_class_init (MMSmsListClass *klass)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (MMSmsListClass, sms_added),
NULL, NULL,
- mm_marshal_VOID__STRING_BOOLEAN,
+ g_cclosure_marshal_generic,
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_BOOLEAN);
signals[SIGNAL_DELETED] =
@@ -490,6 +492,6 @@ mm_sms_list_class_init (MMSmsListClass *klass)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (MMSmsListClass, sms_deleted),
NULL, NULL,
- mm_marshal_VOID__STRING,
+ g_cclosure_marshal_generic,
G_TYPE_NONE, 1, G_TYPE_STRING);
}
diff --git a/src/mm-sms-mbim.c b/src/mm-sms-mbim.c
index c1e51f5..6a26ddc 100644
--- a/src/mm-sms-mbim.c
+++ b/src/mm-sms-mbim.c
@@ -29,6 +29,7 @@
#include "mm-sms-mbim.h"
#include "mm-base-modem.h"
#include "mm-log.h"
+#include "mm-sms-part-3gpp.h"
G_DEFINE_TYPE (MMSmsMbim, mm_sms_mbim, MM_TYPE_SMS)
@@ -154,7 +155,7 @@ sms_send_next_part (SmsSendContext *ctx)
}
/* Get PDU */
- pdu = mm_sms_part_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, &msgstart, &error);
+ pdu = mm_sms_part_3gpp_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, &msgstart, &error);
if (!pdu) {
g_simple_async_result_take_error (ctx->result, error);
sms_send_context_complete_and_free (ctx);
@@ -170,7 +171,7 @@ sms_send_next_part (SmsSendContext *ctx)
NULL);
mbim_device_command (ctx->device,
message,
- 10,
+ 30,
NULL,
(GAsyncReadyCallback)sms_send_set_ready,
ctx);
diff --git a/src/mm-sms-part-3gpp.c b/src/mm-sms-part-3gpp.c
new file mode 100644
index 0000000..b305be5
--- /dev/null
+++ b/src/mm-sms-part-3gpp.c
@@ -0,0 +1,1169 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2011 - 2012 Red Hat, Inc.
+ * Copyright (C) 2012 Google, Inc.
+ */
+
+#include <ctype.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-sms-part-3gpp.h"
+#include "mm-charsets.h"
+#include "mm-log.h"
+
+#define PDU_SIZE 200
+
+#define SMS_TP_MTI_MASK 0x03
+#define SMS_TP_MTI_SMS_DELIVER 0x00
+#define SMS_TP_MTI_SMS_SUBMIT 0x01
+#define SMS_TP_MTI_SMS_STATUS_REPORT 0x02
+
+#define SMS_NUMBER_TYPE_MASK 0x70
+#define SMS_NUMBER_TYPE_UNKNOWN 0x00
+#define SMS_NUMBER_TYPE_INTL 0x10
+#define SMS_NUMBER_TYPE_ALPHA 0x50
+
+#define SMS_NUMBER_PLAN_MASK 0x0f
+#define SMS_NUMBER_PLAN_TELEPHONE 0x01
+
+#define SMS_TP_MMS 0x04
+#define SMS_TP_SRI 0x20
+#define SMS_TP_UDHI 0x40
+#define SMS_TP_RP 0x80
+
+#define SMS_DCS_CODING_MASK 0xec
+#define SMS_DCS_CODING_DEFAULT 0x00
+#define SMS_DCS_CODING_8BIT 0x04
+#define SMS_DCS_CODING_UCS2 0x08
+
+#define SMS_DCS_CLASS_VALID 0x10
+#define SMS_DCS_CLASS_MASK 0x03
+
+#define SMS_TIMESTAMP_LEN 7
+#define SMS_MIN_PDU_LEN (7 + SMS_TIMESTAMP_LEN)
+
+static char sms_bcd_chars[] = "0123456789*#abc\0\0";
+
+static void
+sms_semi_octets_to_bcd_string (char *dest, const guint8 *octets, int num_octets)
+{
+ int i;
+
+ for (i = 0 ; i < num_octets; i++) {
+ *dest++ = sms_bcd_chars[octets[i] & 0xf];
+ *dest++ = sms_bcd_chars[(octets[i] >> 4) & 0xf];
+ }
+ *dest++ = '\0';
+}
+
+static gboolean
+char_to_bcd (char in, guint8 *out)
+{
+ guint32 z;
+
+ if (isdigit (in)) {
+ *out = in - 0x30;
+ return TRUE;
+ }
+
+ for (z = 10; z < 16; z++) {
+ if (in == sms_bcd_chars[z]) {
+ *out = z;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static gsize
+sms_string_to_bcd_semi_octets (guint8 *buf, gsize buflen, const char *string)
+{
+ guint i;
+ guint8 bcd;
+ gsize addrlen, slen;
+
+ addrlen = slen = strlen (string);
+ if (addrlen % 2)
+ addrlen++;
+ g_return_val_if_fail (buflen >= addrlen, 0);
+
+ for (i = 0; i < addrlen; i += 2) {
+ if (!char_to_bcd (string[i], &bcd))
+ return 0;
+ buf[i / 2] = bcd & 0xF;
+
+ if (i >= slen - 1) {
+ /* PDU address gets padded with 0xF if string is odd length */
+ bcd = 0xF;
+ } else if (!char_to_bcd (string[i + 1], &bcd))
+ return 0;
+ buf[i / 2] |= bcd << 4;
+ }
+ return addrlen / 2;
+}
+
+/* len is in semi-octets */
+static char *
+sms_decode_address (const guint8 *address, int len)
+{
+ guint8 addrtype, addrplan;
+ char *utf8;
+
+ addrtype = address[0] & SMS_NUMBER_TYPE_MASK;
+ addrplan = address[0] & SMS_NUMBER_PLAN_MASK;
+ address++;
+
+ if (addrtype == SMS_NUMBER_TYPE_ALPHA) {
+ guint8 *unpacked;
+ guint32 unpacked_len;
+ unpacked = gsm_unpack (address, (len * 4) / 7, 0, &unpacked_len);
+ utf8 = (char *)mm_charset_gsm_unpacked_to_utf8 (unpacked,
+ unpacked_len);
+ g_free(unpacked);
+ } else if (addrtype == SMS_NUMBER_TYPE_INTL &&
+ addrplan == SMS_NUMBER_PLAN_TELEPHONE) {
+ /* International telphone number, format as "+1234567890" */
+ utf8 = g_malloc (len + 3); /* '+' + digits + possible trailing 0xf + NUL */
+ utf8[0] = '+';
+ sms_semi_octets_to_bcd_string (utf8 + 1, address, (len + 1) / 2);
+ } else {
+ /*
+ * All non-alphanumeric types and plans are just digits, but
+ * don't apply any special formatting if we don't know the
+ * format.
+ */
+ utf8 = g_malloc (len + 2); /* digits + possible trailing 0xf + NUL */
+ sms_semi_octets_to_bcd_string (utf8, address, (len + 1) / 2);
+ }
+
+ return utf8;
+}
+
+static char *
+sms_decode_timestamp (const guint8 *timestamp)
+{
+ /* YYMMDDHHMMSS+ZZ */
+ char *timestr;
+ int quarters, hours;
+
+ timestr = g_malloc0 (16);
+ sms_semi_octets_to_bcd_string (timestr, timestamp, 6);
+ quarters = ((timestamp[6] & 0x7) * 10) + ((timestamp[6] >> 4) & 0xf);
+ hours = quarters / 4;
+ if (timestamp[6] & 0x08)
+ timestr[12] = '-';
+ else
+ timestr[12] = '+';
+ timestr[13] = (hours / 10) + '0';
+ timestr[14] = (hours % 10) + '0';
+ /* TODO(njw): Change timestamp rep to something that includes quarter-hours */
+ return timestr;
+}
+
+static MMSmsEncoding
+sms_encoding_type (int dcs)
+{
+ MMSmsEncoding scheme = MM_SMS_ENCODING_UNKNOWN;
+
+ switch ((dcs >> 4) & 0xf) {
+ /* General data coding group */
+ case 0: case 1:
+ case 2: case 3:
+ switch (dcs & 0x0c) {
+ case 0x08:
+ scheme = MM_SMS_ENCODING_UCS2;
+ break;
+ case 0x00:
+ /* fallthrough */
+ /* reserved - spec says to treat it as default alphabet */
+ case 0x0c:
+ scheme = MM_SMS_ENCODING_GSM7;
+ break;
+ case 0x04:
+ scheme = MM_SMS_ENCODING_8BIT;
+ break;
+ }
+ break;
+
+ /* Message waiting group (default alphabet) */
+ case 0xc:
+ case 0xd:
+ scheme = MM_SMS_ENCODING_GSM7;
+ break;
+
+ /* Message waiting group (UCS2 alphabet) */
+ case 0xe:
+ scheme = MM_SMS_ENCODING_UCS2;
+ break;
+
+ /* Data coding/message class group */
+ case 0xf:
+ switch (dcs & 0x04) {
+ case 0x00:
+ scheme = MM_SMS_ENCODING_GSM7;
+ break;
+ case 0x04:
+ scheme = MM_SMS_ENCODING_8BIT;
+ break;
+ }
+ break;
+
+ /* Reserved coding group values - spec says to treat it as default alphabet */
+ default:
+ scheme = MM_SMS_ENCODING_GSM7;
+ break;
+ }
+
+ return scheme;
+}
+
+static char *
+sms_decode_text (const guint8 *text, int len, MMSmsEncoding encoding, int bit_offset)
+{
+ char *utf8;
+ guint8 *unpacked;
+ guint32 unpacked_len;
+
+ if (encoding == MM_SMS_ENCODING_GSM7) {
+ mm_dbg ("Converting SMS part text from GSM7 to UTF8...");
+ unpacked = gsm_unpack ((const guint8 *) text, len, bit_offset, &unpacked_len);
+ utf8 = (char *) mm_charset_gsm_unpacked_to_utf8 (unpacked, unpacked_len);
+ mm_dbg (" Got UTF-8 text: '%s'", utf8);
+ g_free (unpacked);
+ } else if (encoding == MM_SMS_ENCODING_UCS2) {
+ mm_dbg ("Converting SMS part text from UCS-2BE to UTF8...");
+ utf8 = g_convert ((char *) text, len, "UTF8", "UCS-2BE", NULL, NULL, NULL);
+ mm_dbg (" Got UTF-8 text: '%s'", utf8);
+ } else {
+ g_warn_if_reached ();
+ utf8 = g_strdup ("");
+ }
+
+ return utf8;
+}
+
+static guint
+relative_to_validity (guint8 relative)
+{
+ if (relative <= 143)
+ return (relative + 1) * 5;
+
+ if (relative <= 167)
+ return 720 + (relative - 143) * 30;
+
+ return (relative - 166) * 1440;
+}
+
+static guint8
+validity_to_relative (guint validity)
+{
+ if (validity == 0)
+ return 167; /* 24 hours */
+
+ if (validity <= 720) {
+ /* 5 minute units up to 12 hours */
+ if (validity % 5)
+ validity += 5;
+ return (validity / 5) - 1;
+ }
+
+ if (validity > 720 && validity <= 1440) {
+ /* 12 hours + 30 minute units up to 1 day */
+ if (validity % 30)
+ validity += 30; /* round up to next 30 minutes */
+ validity = MIN (validity, 1440);
+ return 143 + ((validity - 720) / 30);
+ }
+
+ if (validity > 1440 && validity <= 43200) {
+ /* 2 days up to 1 month */
+ if (validity % 1440)
+ validity += 1440; /* round up to next day */
+ validity = MIN (validity, 43200);
+ return 167 + ((validity - 1440) / 1440);
+ }
+
+ /* 43200 = 30 days in minutes
+ * 10080 = 7 days in minutes
+ * 635040 = 63 weeks in minutes
+ * 40320 = 4 weeks in minutes
+ */
+ if (validity > 43200 && validity <= 635040) {
+ /* 5 weeks up to 63 weeks */
+ if (validity % 10080)
+ validity += 10080; /* round up to next week */
+ validity = MIN (validity, 635040);
+ return 196 + ((validity - 40320) / 10080);
+ }
+
+ return 255; /* 63 weeks */
+}
+
+MMSmsPart *
+mm_sms_part_3gpp_new_from_pdu (guint index,
+ const gchar *hexpdu,
+ GError **error)
+{
+ gsize pdu_len;
+ guint8 *pdu;
+ MMSmsPart *part;
+
+ /* Convert PDU from hex to binary */
+ pdu = (guint8 *) mm_utils_hexstr2bin (hexpdu, &pdu_len);
+ if (!pdu) {
+ g_set_error_literal (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't convert 3GPP PDU from hex to binary");
+ return NULL;
+ }
+
+ part = mm_sms_part_3gpp_new_from_binary_pdu (index, pdu, pdu_len, error);
+ g_free (pdu);
+
+ return part;
+}
+
+MMSmsPart *
+mm_sms_part_3gpp_new_from_binary_pdu (guint index,
+ const guint8 *pdu,
+ gsize pdu_len,
+ GError **error)
+{
+ MMSmsPart *sms_part;
+ guint8 pdu_type;
+ guint offset;
+ guint smsc_addr_size_bytes;
+ guint tp_addr_size_digits;
+ guint tp_addr_size_bytes;
+ guint8 validity_format = 0;
+ gboolean has_udh = FALSE;
+ /* The following offsets are OPTIONAL, as STATUS REPORTs may not have
+ * them; we use '0' to indicate their absence */
+ guint tp_pid_offset = 0;
+ guint tp_dcs_offset = 0;
+ guint tp_user_data_len_offset = 0;
+ MMSmsEncoding user_data_encoding = MM_SMS_ENCODING_UNKNOWN;
+
+ /* Create the new MMSmsPart */
+ sms_part = mm_sms_part_new (index, MM_SMS_PDU_TYPE_UNKNOWN);
+
+ if (index != SMS_PART_INVALID_INDEX)
+ mm_dbg ("Parsing PDU (%u)...", index);
+ else
+ mm_dbg ("Parsing PDU...");
+
+#define PDU_SIZE_CHECK(required_size, check_descr_str) \
+ if (pdu_len < required_size) { \
+ g_set_error (error, \
+ MM_CORE_ERROR, \
+ MM_CORE_ERROR_FAILED, \
+ "PDU too short, %s: %" G_GSIZE_FORMAT " < %u", \
+ check_descr_str, \
+ pdu_len, \
+ required_size); \
+ mm_sms_part_free (sms_part); \
+ return NULL; \
+ }
+
+ offset = 0;
+
+ /* ---------------------------------------------------------------------- */
+ /* SMSC, in address format, precedes the TPDU
+ * First byte represents the number of BYTES for the address value */
+ PDU_SIZE_CHECK (1, "cannot read SMSC address length");
+ smsc_addr_size_bytes = pdu[offset++];
+ if (smsc_addr_size_bytes > 0) {
+ PDU_SIZE_CHECK (offset + smsc_addr_size_bytes, "cannot read SMSC address");
+ /* SMSC may not be given in DELIVER PDUs */
+ mm_sms_part_take_smsc (sms_part,
+ sms_decode_address (&pdu[1], 2 * (smsc_addr_size_bytes - 1)));
+ mm_dbg (" SMSC address parsed: '%s'", mm_sms_part_get_smsc (sms_part));
+ offset += smsc_addr_size_bytes;
+ } else
+ mm_dbg (" No SMSC address given");
+
+
+ /* ---------------------------------------------------------------------- */
+ /* TP-MTI (1 byte) */
+ PDU_SIZE_CHECK (offset + 1, "cannot read TP-MTI");
+
+ pdu_type = (pdu[offset] & SMS_TP_MTI_MASK);
+ switch (pdu_type) {
+ case SMS_TP_MTI_SMS_DELIVER:
+ mm_dbg (" Deliver type PDU detected");
+ mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_DELIVER);
+ break;
+ case SMS_TP_MTI_SMS_SUBMIT:
+ mm_dbg (" Submit type PDU detected");
+ mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_SUBMIT);
+ break;
+ case SMS_TP_MTI_SMS_STATUS_REPORT:
+ mm_dbg (" Status report type PDU detected");
+ mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_STATUS_REPORT);
+ break;
+ default:
+ mm_sms_part_free (sms_part);
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unhandled message type: 0x%02x",
+ pdu_type);
+ return NULL;
+ }
+
+ /* Delivery report was requested? */
+ if (pdu[offset] & 0x20)
+ mm_sms_part_set_delivery_report_request (sms_part, TRUE);
+
+ /* PDU with validity? (only in SUBMIT PDUs) */
+ if (pdu_type == SMS_TP_MTI_SMS_SUBMIT)
+ validity_format = pdu[offset] & 0x18;
+
+ /* PDU with user data header? */
+ if (pdu[offset] & 0x40)
+ has_udh = TRUE;
+
+ offset++;
+
+ /* ---------------------------------------------------------------------- */
+ /* TP-MR (1 byte, in STATUS_REPORT and SUBMIT PDUs */
+ if (pdu_type == SMS_TP_MTI_SMS_STATUS_REPORT ||
+ pdu_type == SMS_TP_MTI_SMS_SUBMIT) {
+ PDU_SIZE_CHECK (offset + 1, "cannot read message reference");
+
+ mm_dbg (" message reference: %u", (guint)pdu[offset]);
+ mm_sms_part_set_message_reference (sms_part, pdu[offset]);
+ offset++;
+ }
+
+
+ /* ---------------------------------------------------------------------- */
+ /* TP-DA or TP-OA or TP-RA
+ * First byte represents the number of DIGITS in the number.
+ * Round the sender address length up to an even number of
+ * semi-octets, and thus an integral number of octets.
+ */
+ PDU_SIZE_CHECK (offset + 1, "cannot read number of digits in number");
+ tp_addr_size_digits = pdu[offset++];
+ tp_addr_size_bytes = (tp_addr_size_digits + 1) >> 1;
+
+ PDU_SIZE_CHECK (offset + tp_addr_size_bytes, "cannot read number");
+ mm_sms_part_take_number (sms_part,
+ sms_decode_address (&pdu[offset],
+ tp_addr_size_digits));
+ mm_dbg (" Number parsed: '%s'", mm_sms_part_get_number (sms_part));
+ offset += (1 + tp_addr_size_bytes); /* +1 due to the Type of Address byte */
+
+ /* ---------------------------------------------------------------------- */
+ /* Get timestamps and indexes for TP-PID, TP-DCS and TP-UDL/TP-UD */
+
+ if (pdu_type == SMS_TP_MTI_SMS_DELIVER) {
+ PDU_SIZE_CHECK (offset + 9,
+ "cannot read PID/DCS/Timestamp"); /* 1+1+7=9 */
+
+ /* ------ TP-PID (1 byte) ------ */
+ tp_pid_offset = offset++;
+
+ /* ------ TP-DCS (1 byte) ------ */
+ tp_dcs_offset = offset++;
+
+ /* ------ Timestamp (7 bytes) ------ */
+ mm_sms_part_take_timestamp (sms_part,
+ sms_decode_timestamp (&pdu[offset]));
+ offset += 7;
+
+ tp_user_data_len_offset = offset;
+ } else if (pdu_type == SMS_TP_MTI_SMS_SUBMIT) {
+ PDU_SIZE_CHECK (offset + 2 + !!validity_format,
+ "cannot read PID/DCS/Validity"); /* 1+1=2 */
+
+ /* ------ TP-PID (1 byte) ------ */
+ tp_pid_offset = offset++;
+
+ /* ------ TP-DCS (1 byte) ------ */
+ tp_dcs_offset = offset++;
+
+ /* ----------- TP-Validity-Period (1 byte) ----------- */
+ if (validity_format) {
+ switch (validity_format) {
+ case 0x10:
+ mm_dbg (" validity available, format relative");
+ mm_sms_part_set_validity_relative (sms_part,
+ relative_to_validity (pdu[offset]));
+ offset++;
+ break;
+ case 0x08:
+ /* TODO: support enhanced format; GSM 03.40 */
+ mm_dbg (" validity available, format enhanced (not implemented)");
+ /* 7 bytes for enhanced validity */
+ offset += 7;
+ break;
+ case 0x18:
+ /* TODO: support absolute format; GSM 03.40 */
+ mm_dbg (" validity available, format absolute (not implemented)");
+ /* 7 bytes for absolute validity */
+ offset += 7;
+ break;
+ default:
+ /* Cannot happen as we AND with the 0x18 mask */
+ g_assert_not_reached();
+ }
+ }
+
+ tp_user_data_len_offset = offset;
+ }
+ else if (pdu_type == SMS_TP_MTI_SMS_STATUS_REPORT) {
+ /* We have 2 timestamps in status report PDUs:
+ * first, the timestamp for when the PDU was received in the SMSC
+ * second, the timestamp for when the PDU was forwarded by the SMSC
+ */
+ PDU_SIZE_CHECK (offset + 15, "cannot read Timestamps/TP-STATUS"); /* 7+7+1=15 */
+
+ /* ------ Timestamp (7 bytes) ------ */
+ mm_sms_part_take_timestamp (sms_part,
+ sms_decode_timestamp (&pdu[offset]));
+ offset += 7;
+
+ /* ------ Discharge Timestamp (7 bytes) ------ */
+ mm_sms_part_take_discharge_timestamp (sms_part,
+ sms_decode_timestamp (&pdu[offset]));
+ offset += 7;
+
+ /* ----- TP-STATUS (1 byte) ------ */
+ mm_dbg (" delivery state: %u", (guint)pdu[offset]);
+ mm_sms_part_set_delivery_state (sms_part, pdu[offset]);
+ offset++;
+
+ /* ------ TP-PI (1 byte) OPTIONAL ------ */
+ if (offset < pdu_len) {
+ guint next_optional_field_offset = offset + 1;
+
+ /* TP-PID? */
+ if (pdu[offset] & 0x01)
+ tp_pid_offset = next_optional_field_offset++;
+
+ /* TP-DCS? */
+ if (pdu[offset] & 0x02)
+ tp_dcs_offset = next_optional_field_offset++;
+
+ /* TP-UserData? */
+ if (pdu[offset] & 0x04)
+ tp_user_data_len_offset = next_optional_field_offset;
+ }
+ } else
+ g_assert_not_reached ();
+
+ if (tp_pid_offset > 0) {
+ PDU_SIZE_CHECK (tp_pid_offset + 1, "cannot read TP-PID");
+ mm_dbg (" PID: %u", (guint)pdu[tp_pid_offset]);
+ }
+
+ /* Grab user data encoding and message class */
+ if (tp_dcs_offset > 0) {
+ PDU_SIZE_CHECK (tp_dcs_offset + 1, "cannot read TP-DCS");
+
+ /* Encoding given in the 'alphabet' bits */
+ user_data_encoding = sms_encoding_type(pdu[tp_dcs_offset]);
+ switch (user_data_encoding) {
+ case MM_SMS_ENCODING_GSM7:
+ mm_dbg (" user data encoding is GSM7");
+ break;
+ case MM_SMS_ENCODING_UCS2:
+ mm_dbg (" user data encoding is UCS2");
+ break;
+ case MM_SMS_ENCODING_8BIT:
+ mm_dbg (" user data encoding is 8bit");
+ break;
+ default:
+ mm_dbg (" user data encoding is unknown");
+ break;
+ }
+ mm_sms_part_set_encoding (sms_part, user_data_encoding);
+
+ /* Class */
+ if (pdu[tp_dcs_offset] & SMS_DCS_CLASS_VALID)
+ mm_sms_part_set_class (sms_part,
+ pdu[tp_dcs_offset] & SMS_DCS_CLASS_MASK);
+ }
+
+ if (tp_user_data_len_offset > 0) {
+ guint tp_user_data_size_elements;
+ guint tp_user_data_size_bytes;
+ guint tp_user_data_offset;
+ guint bit_offset;
+
+ PDU_SIZE_CHECK (tp_user_data_len_offset + 1, "cannot read TP-UDL");
+ tp_user_data_size_elements = pdu[tp_user_data_len_offset];
+ mm_dbg (" user data length: %u elements", tp_user_data_size_elements);
+
+ if (user_data_encoding == MM_SMS_ENCODING_GSM7)
+ tp_user_data_size_bytes = (7 * (tp_user_data_size_elements + 1 )) / 8;
+ else
+ tp_user_data_size_bytes = tp_user_data_size_elements;
+ mm_dbg (" user data length: %u bytes", tp_user_data_size_bytes);
+
+ tp_user_data_offset = tp_user_data_len_offset + 1;
+ PDU_SIZE_CHECK (tp_user_data_offset + tp_user_data_size_bytes, "cannot read TP-UD");
+
+ bit_offset = 0;
+ if (has_udh) {
+ guint udhl, end;
+
+ udhl = pdu[tp_user_data_offset] + 1;
+ end = tp_user_data_offset + udhl;
+
+ PDU_SIZE_CHECK (tp_user_data_offset + udhl, "cannot read UDH");
+
+ for (offset = tp_user_data_offset + 1; (offset + 1) < end;) {
+ guint8 ie_id, ie_len;
+
+ ie_id = pdu[offset++];
+ ie_len = pdu[offset++];
+
+ switch (ie_id) {
+ case 0x00:
+ if (offset + 2 >= end)
+ break;
+ /*
+ * Ignore the IE if one of the following is true:
+ * - it claims to be part 0 of M
+ * - it claims to be part N of M, N > M
+ */
+ if (pdu[offset + 2] == 0 ||
+ pdu[offset + 2] > pdu[offset + 1])
+ break;
+
+ mm_sms_part_set_concat_reference (sms_part, pdu[offset]);
+ mm_sms_part_set_concat_max (sms_part, pdu[offset + 1]);
+ mm_sms_part_set_concat_sequence (sms_part, pdu[offset + 2]);
+ break;
+ case 0x08:
+ if (offset + 3 >= end)
+ break;
+ /* Concatenated short message, 16-bit reference */
+ if (pdu[offset + 3] == 0 ||
+ pdu[offset + 3] > pdu[offset + 2])
+ break;
+
+ mm_sms_part_set_concat_reference (sms_part, (pdu[offset] << 8) | pdu[offset + 1]);
+ mm_sms_part_set_concat_max (sms_part,pdu[offset + 2]);
+ mm_sms_part_set_concat_sequence (sms_part, pdu[offset + 3]);
+ break;
+ }
+
+ offset += ie_len;
+ }
+
+ /*
+ * Move past the user data headers to prevent it from being
+ * decoded into garbage text.
+ */
+ tp_user_data_offset += udhl;
+ tp_user_data_size_bytes -= udhl;
+ if (user_data_encoding == MM_SMS_ENCODING_GSM7) {
+ /*
+ * Find the number of bits we need to add to the length of the
+ * user data to get a multiple of 7 (the padding).
+ */
+ bit_offset = (7 - udhl % 7) % 7;
+ tp_user_data_size_elements -= (udhl * 8 + bit_offset) / 7;
+ } else
+ tp_user_data_size_elements -= udhl;
+ }
+
+ switch (user_data_encoding) {
+ case MM_SMS_ENCODING_GSM7:
+ case MM_SMS_ENCODING_UCS2:
+ /* Otherwise if it's 7-bit or UCS2 we can decode it */
+ mm_dbg ("Decoding SMS text with '%u' elements", tp_user_data_size_elements);
+ mm_sms_part_take_text (sms_part,
+ sms_decode_text (&pdu[tp_user_data_offset],
+ tp_user_data_size_elements,
+ user_data_encoding,
+ bit_offset));
+ g_warn_if_fail (mm_sms_part_get_text (sms_part) != NULL);
+ break;
+
+ default:
+ {
+ GByteArray *raw;
+
+ mm_dbg ("Skipping SMS text: Unknown encoding (0x%02X)", user_data_encoding);
+
+ PDU_SIZE_CHECK (tp_user_data_offset + tp_user_data_size_bytes, "cannot read user data");
+
+ /* 8-bit encoding is usually binary data, and we have no idea what
+ * actual encoding the data is in so we can't convert it.
+ */
+ raw = g_byte_array_sized_new (tp_user_data_size_bytes);
+ g_byte_array_append (raw, &pdu[tp_user_data_offset], tp_user_data_size_bytes);
+ mm_sms_part_take_data (sms_part, raw);
+ break;
+ }
+ }
+ }
+
+ return sms_part;
+}
+
+/**
+ * mm_sms_part_3gpp_encode_address:
+ *
+ * @address: the phone number to encode
+ * @buf: the buffer to encode @address in
+ * @buflen: the size of @buf
+ * @is_smsc: if %TRUE encode size as number of octets of address infromation,
+ * otherwise if %FALSE encode size as number of digits of @address
+ *
+ * Returns: the size in bytes of the data added to @buf
+ **/
+guint
+mm_sms_part_3gpp_encode_address (const gchar *address,
+ guint8 *buf,
+ gsize buflen,
+ gboolean is_smsc)
+{
+ gsize len;
+
+ g_return_val_if_fail (address != NULL, 0);
+ g_return_val_if_fail (buf != NULL, 0);
+ g_return_val_if_fail (buflen >= 2, 0);
+
+ /* Handle number type & plan */
+ buf[1] = 0x80; /* Bit 7 always 1 */
+ if (address[0] == '+') {
+ buf[1] |= SMS_NUMBER_TYPE_INTL;
+ address++;
+ }
+ buf[1] |= SMS_NUMBER_PLAN_TELEPHONE;
+
+ len = sms_string_to_bcd_semi_octets (&buf[2], buflen, address);
+
+ if (is_smsc)
+ buf[0] = len + 1; /* addr length + size byte */
+ else
+ buf[0] = strlen (address); /* number of digits in address */
+
+ return len ? len + 2 : 0; /* addr length + size byte + number type/plan */
+}
+
+/**
+ * mm_sms_part_3gpp_get_submit_pdu:
+ *
+ * @part: the SMS message part
+ * @out_pdulen: on success, the size of the returned PDU in bytes
+ * @out_msgstart: on success, the byte index in the returned PDU where the
+ * message starts (ie, skipping the SMSC length byte and address, if present)
+ * @error: on error, filled with the error that occurred
+ *
+ * Constructs a single-part SMS message with the given details, preferring to
+ * use the UCS2 character set when the message will fit, otherwise falling back
+ * to the GSM character set.
+ *
+ * Returns: the constructed PDU data on success, or %NULL on error
+ **/
+guint8 *
+mm_sms_part_3gpp_get_submit_pdu (MMSmsPart *part,
+ guint *out_pdulen,
+ guint *out_msgstart,
+ GError **error)
+{
+ guint8 *pdu;
+ guint len, offset = 0;
+ guint shift = 0;
+ guint8 *udl_ptr;
+
+ g_return_val_if_fail (mm_sms_part_get_number (part) != NULL, NULL);
+ g_return_val_if_fail (mm_sms_part_get_text (part) != NULL || mm_sms_part_get_data (part) != NULL, NULL);
+
+ if (mm_sms_part_get_pdu_type (part) != MM_SMS_PDU_TYPE_SUBMIT) {
+ g_set_error (error,
+ MM_MESSAGE_ERROR,
+ MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
+ "Invalid PDU type to generate a 'submit' PDU: '%s'",
+ mm_sms_pdu_type_get_string (mm_sms_part_get_pdu_type (part)));
+ return NULL;
+ }
+
+ mm_dbg ("Creating PDU for part...");
+
+ /* Build up the PDU */
+ pdu = g_malloc0 (PDU_SIZE);
+
+ if (mm_sms_part_get_smsc (part)) {
+ mm_dbg (" adding SMSC to PDU...");
+ len = mm_sms_part_3gpp_encode_address (mm_sms_part_get_smsc (part), pdu, PDU_SIZE, TRUE);
+ if (len == 0) {
+ g_set_error (error,
+ MM_MESSAGE_ERROR,
+ MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
+ "Invalid SMSC address '%s'", mm_sms_part_get_smsc (part));
+ goto error;
+ }
+ offset += len;
+ } else {
+ /* No SMSC, use default */
+ pdu[offset++] = 0x00;
+ }
+
+ if (out_msgstart)
+ *out_msgstart = offset;
+
+ /* ----------- First BYTE ----------- */
+ pdu[offset] = 0;
+
+ /* TP-VP present; format RELATIVE */
+ if (mm_sms_part_get_validity_relative (part) > 0) {
+ mm_dbg (" adding validity to PDU...");
+ pdu[offset] |= 0x10;
+ }
+
+ /* Concatenation sequence only found in multipart SMS */
+ if (mm_sms_part_get_concat_sequence (part)) {
+ mm_dbg (" adding UDHI to PDU...");
+ pdu[offset] |= 0x40; /* UDHI */
+ }
+
+ /* Delivery report requested in singlepart messages or in the last PDU of
+ * multipart messages */
+ if (mm_sms_part_get_delivery_report_request (part) &&
+ (!mm_sms_part_get_concat_sequence (part) ||
+ mm_sms_part_get_concat_max (part) == mm_sms_part_get_concat_sequence (part))) {
+ mm_dbg (" requesting delivery report...");
+ pdu[offset] |= 0x20;
+ }
+
+ /* TP-MTI = SMS-SUBMIT */
+ pdu[offset++] |= 0x01;
+
+
+ /* ----------- TP-MR (1 byte) ----------- */
+
+ pdu[offset++] = 0x00; /* TP-Message-Reference: filled by device */
+
+ /* ----------- Destination address ----------- */
+
+ len = mm_sms_part_3gpp_encode_address (mm_sms_part_get_number (part), &pdu[offset], PDU_SIZE - offset, FALSE);
+ if (len == 0) {
+ g_set_error (error,
+ MM_MESSAGE_ERROR,
+ MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
+ "Invalid number '%s'", mm_sms_part_get_number (part));
+ goto error;
+ }
+ offset += len;
+
+ /* ----------- TP-PID (1 byte) ----------- */
+
+ pdu[offset++] = 0x00;
+
+ /* ----------- TP-DCS (1 byte) ----------- */
+ pdu[offset] = 0x00;
+
+ if (mm_sms_part_get_class (part) >= 0 && mm_sms_part_get_class (part) <= 3) {
+ mm_dbg (" using class %d...", mm_sms_part_get_class (part));
+ pdu[offset] |= SMS_DCS_CLASS_VALID;
+ pdu[offset] |= mm_sms_part_get_class (part);
+ }
+
+ switch (mm_sms_part_get_encoding (part)) {
+ case MM_SMS_ENCODING_UCS2:
+ mm_dbg (" using UCS2 encoding...");
+ pdu[offset] |= SMS_DCS_CODING_UCS2;
+ break;
+ case MM_SMS_ENCODING_GSM7:
+ mm_dbg (" using GSM7 encoding...");
+ pdu[offset] |= SMS_DCS_CODING_DEFAULT; /* GSM */
+ break;
+ default:
+ mm_dbg (" using 8bit encoding...");
+ pdu[offset] |= SMS_DCS_CODING_8BIT;
+ break;
+ }
+ offset++;
+
+ /* ----------- TP-Validity-Period (1 byte): 4 days ----------- */
+ /* Only if TP-VPF was set in first byte */
+
+ if (mm_sms_part_get_validity_relative (part) > 0)
+ pdu[offset++] = validity_to_relative (mm_sms_part_get_validity_relative (part));
+
+ /* ----------- TP-User-Data-Length ----------- */
+ /* Set to zero initially, and keep a ptr for easy access later */
+ udl_ptr = &pdu[offset];
+ pdu[offset++] = 0;
+
+ /* Build UDH */
+ if (mm_sms_part_get_concat_sequence (part)) {
+ mm_dbg (" adding UDH header in PDU... (reference: %u, max: %u, sequence: %u)",
+ mm_sms_part_get_concat_reference (part),
+ mm_sms_part_get_concat_max (part),
+ mm_sms_part_get_concat_sequence (part));
+ pdu[offset++] = 0x05; /* udh len */
+ pdu[offset++] = 0x00; /* mid */
+ pdu[offset++] = 0x03; /* data len */
+ pdu[offset++] = (guint8)mm_sms_part_get_concat_reference (part);
+ pdu[offset++] = (guint8)mm_sms_part_get_concat_max (part);
+ pdu[offset++] = (guint8)mm_sms_part_get_concat_sequence (part);
+
+ /* if a UDH is present and the data encoding is the default 7-bit
+ * alphabet, the user data must be 7-bit word aligned after the
+ * UDH. This means up to 6 bits of zeros need to be inserted at the
+ * start of the message.
+ *
+ * In our case the UDH is 6 bytes long, 48bits. The next multiple of
+ * 7 is therefore 49, so we only need to include one bit of padding.
+ */
+ shift = 1;
+ }
+
+ if (mm_sms_part_get_encoding (part) == MM_SMS_ENCODING_GSM7) {
+ guint8 *unpacked, *packed;
+ guint32 unlen = 0, packlen = 0;
+
+ unpacked = mm_charset_utf8_to_unpacked_gsm (mm_sms_part_get_text (part), &unlen);
+ if (!unpacked || unlen == 0) {
+ g_free (unpacked);
+ g_set_error_literal (error,
+ MM_MESSAGE_ERROR,
+ MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
+ "Failed to convert message text to GSM");
+ goto error;
+ }
+
+ /* Set real data length, in septets
+ * If we had UDH, add 7 septets
+ */
+ *udl_ptr = mm_sms_part_get_concat_sequence (part) ? (7 + unlen) : unlen;
+ mm_dbg (" user data length is '%u' septets (%s UDH)",
+ *udl_ptr,
+ mm_sms_part_get_concat_sequence (part) ? "with" : "without");
+
+ packed = gsm_pack (unpacked, unlen, shift, &packlen);
+ g_free (unpacked);
+ if (!packed || packlen == 0) {
+ g_free (packed);
+ g_set_error_literal (error,
+ MM_MESSAGE_ERROR,
+ MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
+ "Failed to pack message text to GSM");
+ goto error;
+ }
+
+ memcpy (&pdu[offset], packed, packlen);
+ g_free (packed);
+ offset += packlen;
+ } else if (mm_sms_part_get_encoding (part) == MM_SMS_ENCODING_UCS2) {
+ GByteArray *array;
+
+ /* Try to guess a good value for the array */
+ array = g_byte_array_sized_new (strlen (mm_sms_part_get_text (part)) * 2);
+ if (!mm_modem_charset_byte_array_append (array, mm_sms_part_get_text (part), FALSE, MM_MODEM_CHARSET_UCS2)) {
+ g_byte_array_free (array, TRUE);
+ g_set_error_literal (error,
+ MM_MESSAGE_ERROR,
+ MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
+ "Failed to convert message text to UCS2");
+ goto error;
+ }
+
+ /* Set real data length, in octets
+ * If we had UDH, add 6 octets
+ */
+ *udl_ptr = mm_sms_part_get_concat_sequence (part) ? (6 + array->len) : array->len;
+ mm_dbg (" user data length is '%u' octets (%s UDH)",
+ *udl_ptr,
+ mm_sms_part_get_concat_sequence (part) ? "with" : "without");
+
+ memcpy (&pdu[offset], array->data, array->len);
+ offset += array->len;
+ g_byte_array_free (array, TRUE);
+ } else if (mm_sms_part_get_encoding (part) == MM_SMS_ENCODING_8BIT) {
+ const GByteArray *data;
+
+ data = mm_sms_part_get_data (part);
+
+ /* Set real data length, in octets
+ * If we had UDH, add 6 octets
+ */
+ *udl_ptr = mm_sms_part_get_concat_sequence (part) ? (6 + data->len) : data->len;
+ mm_dbg (" binary user data length is '%u' octets (%s UDH)",
+ *udl_ptr,
+ mm_sms_part_get_concat_sequence (part) ? "with" : "without");
+
+ memcpy (&pdu[offset], data->data, data->len);
+ offset += data->len;
+ } else
+ g_assert_not_reached ();
+
+ if (out_pdulen)
+ *out_pdulen = offset;
+ return pdu;
+
+error:
+ g_free (pdu);
+ return NULL;
+}
+
+gchar **
+mm_sms_part_3gpp_util_split_text (const gchar *text,
+ MMSmsEncoding *encoding)
+{
+ guint gsm_unsupported = 0;
+ gchar **out;
+ guint n_chunks;
+ guint i;
+ guint j;
+ gsize in_len;
+
+ if (!text)
+ return NULL;
+
+ in_len = strlen (text);
+
+ /* Some info about the rules for splitting.
+ *
+ * The User Data can be up to 140 bytes in the SMS part:
+ * 0) If we only need one chunk, it can be of up to 140 bytes.
+ * If we need more than one chunk, these have to be of 140 - 6 = 134
+ * bytes each, as we need place for the UDH header.
+ * 1) If we're using GSM7 encoding, this gives us up to 160 characters,
+ * as we can pack 160 characters of 7bits each into 140 bytes.
+ * 160 * 7 = 140 * 8 = 1120.
+ * If we only have 134 bytes allowed, that would mean that we can pack
+ * up to 153 input characters:
+ * 134 * 8 = 1072; 1072/7=153.14
+ * 2) If we're using UCS2 encoding, we can pack up to 70 characters in
+ * 140 bytes (each with 2 bytes), or up to 67 characters in 134 bytes.
+ *
+ * This method does the split of the input string into N strings, so that
+ * each of the strings can be placed in a SMS part.
+ */
+
+ /* Check if we can do GSM encoding */
+ mm_charset_get_encoded_len (text,
+ MM_MODEM_CHARSET_GSM,
+ &gsm_unsupported);
+ if (gsm_unsupported > 0) {
+ /* If cannot do it in GSM encoding, do it in UCS-2 */
+ GByteArray *array;
+
+ *encoding = MM_SMS_ENCODING_UCS2;
+
+ /* Guess more or less the size of the output array to avoid multiple
+ * allocations */
+ array = g_byte_array_sized_new (in_len * 2);
+ if (!mm_modem_charset_byte_array_append (array,
+ text,
+ FALSE,
+ MM_MODEM_CHARSET_UCS2)) {
+ g_byte_array_unref (array);
+ return NULL;
+ }
+
+ /* Our bytearray has it in UCS-2 now.
+ * UCS-2 is a fixed-size encoding, which means that the text has exactly
+ * 2 bytes for each unicode point. We can now split this array into
+ * chunks of 67 UCS-2 characters (134 bytes).
+ *
+ * Note that UCS-2 covers unicode points between U+0000 and U+FFFF, which
+ * means that there is no direct relationship between the size of the
+ * input text in UTF-8 and the size of the text in UCS-2. A 3-byte UTF-8
+ * encoded character will still be represented with 2 bytes in UCS-2.
+ */
+ if (array->len <= 140) {
+ out = g_new (gchar *, 2);
+ out[0] = g_strdup (text);
+ out[1] = NULL;
+ } else {
+ n_chunks = array->len / 134;
+ if (array->len % 134 != 0)
+ n_chunks++;
+
+ out = g_new0 (gchar *, n_chunks + 1);
+ for (i = 0, j = 0; i < n_chunks; i++, j += 134) {
+ out[i] = sms_decode_text (&array->data[j],
+ MIN (array->len - j, 134),
+ MM_SMS_ENCODING_UCS2,
+ 0);
+ }
+ }
+ g_byte_array_unref (array);
+ } else {
+ /* Do it with GSM encoding */
+ *encoding = MM_SMS_ENCODING_GSM7;
+
+ if (in_len <= 160) {
+ out = g_new (gchar *, 2);
+ out[0] = g_strdup (text);
+ out[1] = NULL;
+ } else {
+ n_chunks = in_len / 153;
+ if (in_len % 153 != 0)
+ n_chunks++;
+
+ out = g_new0 (gchar *, n_chunks + 1);
+ for (i = 0, j = 0; i < n_chunks; i++, j += 153) {
+ out[i] = g_strndup (&text[j], 153);
+ }
+ }
+ }
+
+ return out;
+}
+
+GByteArray **
+mm_sms_part_3gpp_util_split_data (const guint8 *data,
+ gsize data_len)
+{
+ GByteArray **out;
+
+ /* Some info about the rules for splitting.
+ *
+ * The User Data can be up to 140 bytes in the SMS part:
+ * 0) If we only need one chunk, it can be of up to 140 bytes.
+ * If we need more than one chunk, these have to be of 140 - 6 = 134
+ * bytes each, as we need place for the UDH header.
+ */
+
+ if (data_len <= 140) {
+ out = g_new0 (GByteArray *, 2);
+ out[0] = g_byte_array_append (g_byte_array_sized_new (data_len),
+ data,
+ data_len);
+ } else {
+ guint n_chunks;
+ guint i;
+ guint j;
+
+ n_chunks = data_len / 134;
+ if (data_len % 134 != 0)
+ n_chunks ++;
+
+ out = g_new0 (GByteArray *, n_chunks + 1);
+ for (i = 0, j = 0; i < n_chunks; i++, j+= 134) {
+ out[i] = g_byte_array_append (g_byte_array_sized_new (134),
+ &data[j],
+ MIN (data_len - j, 134));
+ }
+ }
+
+ return out;
+}
diff --git a/src/mm-sms-part-3gpp.h b/src/mm-sms-part-3gpp.h
new file mode 100644
index 0000000..82709a2
--- /dev/null
+++ b/src/mm-sms-part-3gpp.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2011 - 2012 Red Hat, Inc.
+ * Copyright (C) 2013 Google, Inc.
+ */
+
+#ifndef MM_SMS_PART_3GPP_H
+#define MM_SMS_PART_3GPP_H
+
+#include <glib.h>
+#include <ModemManager-enums.h>
+
+#include "mm-sms-part.h"
+
+#define MM_SMS_PART_3GPP_MAX_PDU_LEN 344
+
+MMSmsPart *mm_sms_part_3gpp_new_from_pdu (guint index,
+ const gchar *hexpdu,
+ GError **error);
+
+MMSmsPart *mm_sms_part_3gpp_new_from_binary_pdu (guint index,
+ const guint8 *pdu,
+ gsize pdu_len,
+ GError **error);
+
+guint8 *mm_sms_part_3gpp_get_submit_pdu (MMSmsPart *part,
+ guint *out_pdulen,
+ guint *out_msgstart,
+ GError **error);
+
+/* For testcases only */
+
+guint mm_sms_part_3gpp_encode_address (const gchar *address,
+ guint8 *buf,
+ gsize buflen,
+ gboolean is_smsc);
+
+gchar **mm_sms_part_3gpp_util_split_text (const gchar *text,
+ MMSmsEncoding *encoding);
+
+GByteArray **mm_sms_part_3gpp_util_split_data (const guint8 *data,
+ gsize data_len);
+
+#endif /* MM_SMS_PART_3GPP_H */
diff --git a/src/mm-sms-part-cdma.c b/src/mm-sms-part-cdma.c
new file mode 100644
index 0000000..2f0c99a
--- /dev/null
+++ b/src/mm-sms-part-cdma.c
@@ -0,0 +1,1616 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2013 Google, Inc.
+ */
+
+#include <ctype.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-charsets.h"
+#include "mm-sms-part-cdma.h"
+#include "mm-log.h"
+
+/*
+ * Documentation that you may want to have around:
+ *
+ * 3GPP2 C.S0015-B: Short Message Service (SMS) for Wideband Spread Spectrum
+ * Systems.
+ *
+ * 3GPP2 C.R1001-G: Administration of Parameter Value Assignments for CDMA2000
+ * Spread Spectrum Standards.
+ *
+ * 3GPP2 X.S0004-550-E: Mobile Application Part (MAP).
+ *
+ * 3GPP2 C.S0005-E: Upper Layer (Layer 3) Signaling Standard for CDMA2000
+ * Spread Spectrum Systems.
+ *
+ * 3GPP2 N.S0005-O: Cellular Radiotelecommunications Intersystem Operations.
+ */
+
+/* 3GPP2 C.S0015-B, section 3.4, table 3.4-1 */
+typedef enum {
+ MESSAGE_TYPE_POINT_TO_POINT = 0,
+ MESSAGE_TYPE_BROADCAST = 1,
+ MESSAGE_TYPE_ACKNOWLEDGE = 2
+} MessageType;
+
+/* 3GPP2 C.S0015-B, section 3.4.3, table 3.4.3-1 */
+typedef enum {
+ PARAMETER_ID_TELESERVICE_ID = 0,
+ PARAMETER_ID_SERVICE_CATEGORY = 1,
+ PARAMETER_ID_ORIGINATING_ADDRESS = 2,
+ PARAMETER_ID_ORIGINATING_SUBADDRESS = 3,
+ PARAMETER_ID_DESTINATION_ADDRESS = 4,
+ PARAMETER_ID_DESTINATION_SUBADDRESS = 5,
+ PARAMETER_ID_BEARER_REPLY_OPTION = 6,
+ PARAMETER_ID_CAUSE_CODES = 7,
+ PARAMETER_ID_BEARER_DATA = 8
+} ParameterId;
+
+/* 3GPP2 C.S0015-B, section 3.4.3.3 */
+typedef enum {
+ DIGIT_MODE_DTMF = 0,
+ DIGIT_MODE_ASCII = 1
+} DigitMode;
+
+/* 3GPP2 C.S0015-B, section 3.4.3.3 */
+typedef enum {
+ NUMBER_MODE_DIGIT = 0,
+ NUMBER_MODE_DATA_NETWORK_ADDRESS = 1
+} NumberMode;
+
+/* 3GPP2 C.S0005-E, section 2.7.1.3.2.4, table 2.7.1.3.2.4-2 */
+typedef enum {
+ NUMBER_TYPE_UNKNOWN = 0,
+ NUMBER_TYPE_INTERNATIONAL = 1,
+ NUMBER_TYPE_NATIONAL = 2,
+ NUMBER_TYPE_NETWORK_SPECIFIC = 3,
+ NUMBER_TYPE_SUBSCRIBER = 4,
+ /* 5 reserved */
+ NUMBER_TYPE_ABBREVIATED = 6,
+ /* 7 reserved */
+} NumberType;
+
+/* 3GPP2 C.S0015-B, section 3.4.3.3, table 3.4.3.3-1 */
+typedef enum {
+ DATA_NETWORK_ADDRESS_TYPE_UNKNOWN = 0,
+ DATA_NETWORK_ADDRESS_TYPE_INTERNET_PROTOCOL = 1,
+ DATA_NETWORK_ADDRESS_TYPE_INTERNET_EMAIL_ADDRESS = 2
+} DataNetworkAddressType;
+
+/* 3GPP2 C.S0005-E, section 2.7.1.3.2.4, table 2.7.1.3.2.4-3 */
+typedef enum {
+ NUMBERING_PLAN_UNKNOWN = 0,
+ NUMBERING_PLAN_ISDN = 1,
+ NUMBERING_PLAN_DATA = 3,
+ NUMBERING_PLAN_TELEX = 4,
+ NUMBERING_PLAN_PRIVATE = 9,
+ /* 15 reserved */
+} NumberingPlan;
+
+/* 3GPP2 C.S0015-B, section 3.4.3.6 */
+typedef enum {
+ ERROR_CLASS_NO_ERROR = 0,
+ /* 1 reserved */
+ ERROR_CLASS_TEMPORARY = 2,
+ ERROR_CLASS_PERMANENT = 3
+} ErrorClass;
+
+/* 3GPP2 N.S0005-O, section 6.5.2.125*/
+typedef enum {
+ CAUSE_CODE_NETWORK_PROBLEM_ADDRESS_VACANT = 0,
+ CAUSE_CODE_NETWORK_PROBLEM_ADDRESS_TRANSLATION_FAILURE = 1,
+ CAUSE_CODE_NETWORK_PROBLEM_NETWORK_RESOURCE_OUTAGE = 2,
+ CAUSE_CODE_NETWORK_PROBLEM_NETWORK_FAILURE = 3,
+ CAUSE_CODE_NETWORK_PROBLEM_INVALID_TELESERVICE_ID = 4,
+ CAUSE_CODE_NETWORK_PROBLEM_OTHER = 5,
+ /* 6 to 31 reserved, treat as CAUSE_CODE_NETWORK_PROBLEM_OTHER */
+ CAUSE_CODE_TERMINAL_PROBLEM_NO_PAGE_RESPONSE = 32,
+ CAUSE_CODE_TERMINAL_PROBLEM_DESTINATION_BUSY = 33,
+ CAUSE_CODE_TERMINAL_PROBLEM_NO_ACKNOWLEDGMENT = 34,
+ CAUSE_CODE_TERMINAL_PROBLEM_DESTINATION_RESOURCE_SHORTAGE = 35,
+ CAUSE_CODE_TERMINAL_PROBLEM_SMS_DELIVERY_POSTPONED = 36,
+ CAUSE_CODE_TERMINAL_PROBLEM_DESTINATION_OUT_OF_SERVICE = 37,
+ CAUSE_CODE_TERMINAL_PROBLEM_DESTINATION_NO_LONGER_AT_THIS_ADDRESS = 38,
+ CAUSE_CODE_TERMINAL_PROBLEM_OTHER = 39,
+ /* 40 to 47 reserved, treat as CAUSE_CODE_TERMINAL_PROBLEM_OTHER */
+ /* 48 to 63 reserved, treat as CAUSE_CODE_TERMINAL_PROBLEM_SMS_DELIVERY_POSTPONED */
+ CAUSE_CODE_RADIO_INTERFACE_PROBLEM_RESOURCE_SHORTAGE = 64,
+ CAUSE_CODE_RADIO_INTERFACE_PROBLEM_INCOMPATIBILITY = 65,
+ CAUSE_CODE_RADIO_INTERFACE_PROBLEM_OTHER = 66,
+ /* 67 to 95 reserved, treat as CAUSE_CODE_RADIO_INTERFACE_PROBLEM_OTHER */
+ CAUSE_CODE_GENERAL_PROBLEM_ENCODING = 96,
+ CAUSE_CODE_GENERAL_PROBLEM_SMS_ORIGINATION_DENIED = 97,
+ CAUSE_CODE_GENERAL_PROBLEM_SMS_TERMINATION_DENIED = 98,
+ CAUSE_CODE_GENERAL_PROBLEM_SUPPLEMENTARY_SERVICE_NOT_SUPPORTED = 99,
+ CAUSE_CODE_GENERAL_PROBLEM_SMS_NOT_SUPPORTED = 100,
+ /* 101 reserved */
+ CAUSE_CODE_GENERAL_PROBLEM_MISSING_EXPECTED_PARAMETER = 102,
+ CAUSE_CODE_GENERAL_PROBLEM_MISSING_MANDATORY_PARAMETER = 103,
+ CAUSE_CODE_GENERAL_PROBLEM_UNRECOGNIZED_PARAMETER_VALUE = 104,
+ CAUSE_CODE_GENERAL_PROBLEM_UNEXPECTED_PARAMETER_VALUE = 105,
+ CAUSE_CODE_GENERAL_PROBLEM_USER_DATA_SIZE_ERROR = 106,
+ CAUSE_CODE_GENERAL_PROBLEM_OTHER = 107,
+ /* 108 to 223 reserved, treat as CAUSE_CODE_GENERAL_PROBLEM_OTHER */
+ /* 224 to 255 reserved for TIA/EIA-41 extension, otherwise treat as CAUSE_CODE_GENERAL_PROBLEM_OTHER */
+} CauseCode;
+
+/* 3GPP2 C.S0015-B, section 4.5, table 4.5-1 */
+typedef enum {
+ SUBPARAMETER_ID_MESSAGE_ID = 0,
+ SUBPARAMETER_ID_USER_DATA = 1,
+ SUBPARAMETER_ID_USER_RESPONSE_CODE = 2,
+ SUBPARAMETER_ID_MESSAGE_CENTER_TIME_STAMP = 3,
+ SUBPARAMETER_ID_VALIDITY_PERIOD_ABSOLUTE = 4,
+ SUBPARAMETER_ID_VALIDITY_PERIOD_RELATIVE = 5,
+ SUBPARAMETER_ID_DEFERRED_DELIVERY_TIME_ABSOLUTE = 6,
+ SUBPARAMETER_ID_DEFERRED_DELIVERY_TIME_RELATIVE = 7,
+ SUBPARAMETER_ID_PRIORITY_INDICATOR = 8,
+ SUBPARAMETER_ID_PRIVACY_INDICATOR = 9,
+ SUBPARAMETER_ID_REPLY_OPTION = 10,
+ SUBPARAMETER_ID_NUMBER_OF_MESSAGES = 11,
+ SUBPARAMETER_ID_ALERT_ON_MESSAGE_DELIVERY = 12,
+ SUBPARAMETER_ID_LANGUAGE_INDICATOR = 13,
+ SUBPARAMETER_ID_CALL_BACK_NUMBER = 14,
+ SUBPARAMETER_ID_MESSAGE_DISPLAY_MODE = 15,
+ SUBPARAMETER_ID_MULTIPLE_ENCODING_USER_DATA = 16,
+ SUBPARAMETER_ID_MESSAGE_DEPOSIT_INDEX = 17,
+ SUBPARAMETER_ID_SERVICE_CATEGORY_PROGRAM_DATA = 18,
+ SUBPARAMETER_ID_SERVICE_CATEGORY_PROGRAM_RESULT = 19,
+ SUBPARAMETER_ID_MESSAGE_STATUS = 20,
+ SUBPARAMETER_ID_TP_FAILURE_CAUSE = 21,
+ SUBPARAMETER_ID_ENHANCED_VMN = 22,
+ SUBPARAMETER_ID_ENHANCED_VMN_ACK = 23,
+} SubparameterId;
+
+/* 3GPP2 C.S0015-B, section 4.5.1, table 4.5.1-1 */
+typedef enum {
+ TELESERVICE_MESSAGE_TYPE_UNKNOWN = 0,
+ TELESERVICE_MESSAGE_TYPE_DELIVER = 1,
+ TELESERVICE_MESSAGE_TYPE_SUBMIT = 2,
+ TELESERVICE_MESSAGE_TYPE_CANCELLATION = 3,
+ TELESERVICE_MESSAGE_TYPE_DELIVERY_ACKNOWLEDGEMENT = 4,
+ TELESERVICE_MESSAGE_TYPE_USER_ACKNOWLEDGEMENT = 5,
+ TELESERVICE_MESSAGE_TYPE_READ_ACKNOWLEDGEMENT = 6,
+} TeleserviceMessageType;
+
+/* C.R1001-G, section 9.1, table 9.1-1 */
+typedef enum {
+ ENCODING_OCTET = 0,
+ ENCODING_EXTENDED_PROTOCOL_MESSAGE = 1,
+ ENCODING_ASCII_7BIT = 2,
+ ENCODING_IA5 = 3,
+ ENCODING_UNICODE = 4,
+ ENCODING_SHIFT_JIS = 5,
+ ENCODING_KOREAN = 6,
+ ENCODING_LATIN_HEBREW = 7,
+ ENCODING_LATIN = 8,
+ ENCODING_GSM_7BIT = 9,
+ ENCODING_GSM_DCS = 10,
+} Encoding;
+
+static const gchar *
+encoding_to_string (Encoding encoding)
+{
+ static const gchar *encoding_str[] = {
+ "octet",
+ "extend protocol message",
+ "7-bit ASCII",
+ "IA5",
+ "unicode",
+ "shift-j is",
+ "korean",
+ "latin/hebrew",
+ "latin",
+ "7-bit GSM",
+ "GSM data coding scheme"
+ };
+
+ if (encoding >= ENCODING_OCTET && encoding <= ENCODING_GSM_DCS)
+ return encoding_str[encoding];
+
+ return "unknown";
+}
+
+/*****************************************************************************/
+/* Read bits; o_bits < 8; n_bits <= 8
+ *
+ * Byte 0 Byte 1
+ * [7|6|5|4|3|2|1|0] [7|6|5|4|3|2|1|0]
+ *
+ * o_bits+n_bits <= 16
+ *
+ */
+static guint8
+read_bits (const guint8 *bytes,
+ guint8 o_bits,
+ guint8 n_bits)
+{
+ guint8 bits_in_first;
+ guint8 bits_in_second;
+
+ g_assert (o_bits < 8);
+ g_assert (n_bits <= 8);
+ g_assert (o_bits + n_bits <= 16);
+
+ /* Read only from the first byte */
+ if (o_bits + n_bits <= 8)
+ return (bytes[0] >> (8 - o_bits - n_bits)) & ((1 << n_bits) - 1);
+
+ /* Read (8 - o_bits) from the first byte and (n_bits - (8 - o_bits)) from the second byte */
+ bits_in_first = 8 - o_bits;
+ bits_in_second = n_bits - bits_in_first;
+ return (read_bits (&bytes[0], o_bits, bits_in_first) << bits_in_second) | read_bits (&bytes[1], 0, bits_in_second);
+}
+
+/*****************************************************************************/
+/* Cause code to delivery state */
+
+static MMSmsDeliveryState
+cause_code_to_delivery_state (guint8 error_class,
+ guint8 cause_code)
+{
+ guint delivery_state = 0;
+
+ switch (error_class) {
+ case ERROR_CLASS_NO_ERROR:
+ return MM_SMS_DELIVERY_STATE_COMPLETED_RECEIVED;
+ case ERROR_CLASS_TEMPORARY:
+ delivery_state += 0x300;
+ case ERROR_CLASS_PERMANENT:
+ delivery_state += 0x200;
+ default:
+ return MM_SMS_DELIVERY_STATE_UNKNOWN;
+ }
+
+ /* Fixes for unknown cause codes */
+
+ if (cause_code >= 6 && cause_code <= 31)
+ /* 6 to 31 reserved, treat as CAUSE_CODE_NETWORK_PROBLEM_OTHER */
+ delivery_state += CAUSE_CODE_NETWORK_PROBLEM_OTHER;
+ else if (cause_code >= 40 && cause_code <= 47)
+ /* 40 to 47 reserved, treat as CAUSE_CODE_TERMINAL_PROBLEM_OTHER */
+ delivery_state += CAUSE_CODE_TERMINAL_PROBLEM_OTHER;
+ else if (cause_code >= 48 && cause_code <= 63)
+ /* 48 to 63 reserved, treat as CAUSE_CODE_TERMINAL_PROBLEM_SMS_DELIVERY_POSTPONED */
+ delivery_state += CAUSE_CODE_TERMINAL_PROBLEM_SMS_DELIVERY_POSTPONED;
+ else if (cause_code >= 67 && cause_code <= 95)
+ /* 67 to 95 reserved, treat as CAUSE_CODE_RADIO_INTERFACE_PROBLEM_OTHER */
+ delivery_state += CAUSE_CODE_RADIO_INTERFACE_PROBLEM_OTHER;
+ else if (cause_code == 101)
+ /* 101 reserved */
+ delivery_state += CAUSE_CODE_GENERAL_PROBLEM_OTHER;
+ else if (cause_code >= 108 && cause_code <= 255)
+ /* 108 to 223 reserved, treat as CAUSE_CODE_GENERAL_PROBLEM_OTHER
+ * 224 to 255 reserved for TIA/EIA-41 extension, otherwise treat as CAUSE_CODE_GENERAL_PROBLEM_OTHER */
+ delivery_state += CAUSE_CODE_GENERAL_PROBLEM_OTHER;
+ else
+ /* direct relationship */
+ delivery_state += cause_code;
+
+ return (MMSmsDeliveryState) delivery_state;
+}
+
+/*****************************************************************************/
+
+MMSmsPart *
+mm_sms_part_cdma_new_from_pdu (guint index,
+ const gchar *hexpdu,
+ GError **error)
+{
+ gsize pdu_len;
+ guint8 *pdu;
+ MMSmsPart *part;
+
+ /* Convert PDU from hex to binary */
+ pdu = (guint8 *) mm_utils_hexstr2bin (hexpdu, &pdu_len);
+ if (!pdu) {
+ g_set_error_literal (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't convert CDMA PDU from hex to binary");
+ return NULL;
+ }
+
+ part = mm_sms_part_cdma_new_from_binary_pdu (index, pdu, pdu_len, error);
+ g_free (pdu);
+
+ return part;
+}
+
+struct Parameter {
+ guint8 parameter_id;
+ guint8 parameter_len;
+ guint8 parameter_value[];
+} __attribute__((packed));
+
+static void
+read_teleservice_id (MMSmsPart *sms_part,
+ const struct Parameter *parameter)
+{
+ guint16 teleservice_id;
+
+ g_assert (parameter->parameter_id == PARAMETER_ID_TELESERVICE_ID);
+
+ if (parameter->parameter_len != 2) {
+ mm_dbg (" invalid teleservice ID length found (%u != 2): ignoring",
+ parameter->parameter_len);
+ return;
+ }
+
+ memcpy (&teleservice_id, &parameter->parameter_value[0], 2);
+ teleservice_id = GUINT16_FROM_BE (teleservice_id);
+
+ switch (teleservice_id){
+ case MM_SMS_CDMA_TELESERVICE_ID_CMT91:
+ case MM_SMS_CDMA_TELESERVICE_ID_WPT:
+ case MM_SMS_CDMA_TELESERVICE_ID_WMT:
+ case MM_SMS_CDMA_TELESERVICE_ID_VMN:
+ case MM_SMS_CDMA_TELESERVICE_ID_WAP:
+ case MM_SMS_CDMA_TELESERVICE_ID_WEMT:
+ case MM_SMS_CDMA_TELESERVICE_ID_SCPT:
+ case MM_SMS_CDMA_TELESERVICE_ID_CATPT:
+ break;
+ default:
+ mm_dbg (" invalid teleservice ID found (%u): ignoring", teleservice_id);
+ return;
+ }
+
+ mm_dbg (" teleservice ID: %s (%u)",
+ mm_sms_cdma_teleservice_id_get_string (teleservice_id),
+ teleservice_id);
+
+ mm_sms_part_set_cdma_teleservice_id (sms_part,
+ (MMSmsCdmaTeleserviceId)teleservice_id);
+}
+
+static void
+read_service_category (MMSmsPart *sms_part,
+ const struct Parameter *parameter)
+{
+ guint16 service_category;
+
+ g_assert (parameter->parameter_id == PARAMETER_ID_SERVICE_CATEGORY);
+
+ if (parameter->parameter_len != 2) {
+ mm_dbg (" invalid service category length found (%u != 2): ignoring",
+ parameter->parameter_len);
+ return;
+ }
+
+ memcpy (&service_category, &parameter->parameter_value[0], 2);
+ service_category = GUINT16_FROM_BE (service_category);
+
+ switch (service_category) {
+ case MM_SMS_CDMA_SERVICE_CATEGORY_EMERGENCY_BROADCAST:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_ADMINISTRATIVE:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_MAINTENANCE:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_GENERAL_NEWS_LOCAL:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_GENERAL_NEWS_REGIONAL:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_GENERAL_NEWS_NATIONAL:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_GENERAL_NEWS_INTERNATIONAL:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_BUSINESS_NEWS_LOCAL:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_BUSINESS_NEWS_REGIONAL:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_BUSINESS_NEWS_NATIONAL:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_BUSINESS_NEWS_INTERNATIONAL:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_SPORTS_NEWS_LOCAL:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_SPORTS_NEWS_REGIONAL:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_SPORTS_NEWS_NATIONAL:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_SPORTS_NEWS_INTERNATIONAL:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_ENTERTAINMENT_NEWS_LOCAL:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_ENTERTAINMENT_NEWS_REGIONAL:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_ENTERTAINMENT_NEWS_NATIONAL:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_ENTERTAINMENT_NEWS_INTERNATIONAL:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_LOCAL_WEATHER:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_TRAFFIC_REPORT:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_FLIGHT_SCHEDULES:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_RESTAURANTS:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_LODGINGS:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_RETAIL_DIRECTORY:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_ADVERTISEMENTS:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_STOCK_QUOTES:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_EMPLOYMENT:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_HOSPITALS:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_TECHNOLOGY_NEWS:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_MULTICATEGORY:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_PRESIDENTIAL_ALERT:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_EXTREME_THREAT:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_SEVERE_THREAT:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY:
+ case MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_TEST:
+ break;
+ default:
+ mm_dbg (" invalid service category found (%u): ignoring", service_category);
+ return;
+ }
+
+ mm_dbg (" service category: %s (%u)",
+ mm_sms_cdma_service_category_get_string (service_category),
+ service_category);
+
+ mm_sms_part_set_cdma_service_category (sms_part,
+ (MMSmsCdmaServiceCategory)service_category);
+}
+
+static guint8
+dtmf_to_ascii (guint8 dtmf)
+{
+ static const gchar dtmf_to_ascii_digits[13] = {
+ '\0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '*', '#' };
+
+ if (dtmf > 0 && dtmf < 13)
+ return dtmf_to_ascii_digits[dtmf];
+
+ mm_dbg (" invalid dtmf digit: %u", dtmf);
+ return '\0';
+}
+
+static void
+read_address (MMSmsPart *sms_part,
+ const struct Parameter *parameter)
+{
+ guint8 digit_mode;
+ guint8 number_mode;
+ guint8 number_type;
+ guint8 numbering_plan;
+ guint8 num_fields;
+ guint byte_offset = 0;
+ guint bit_offset = 0;
+ guint i;
+ gchar *number = NULL;
+
+#define OFFSETS_UPDATE(n_bits) do { \
+ bit_offset += n_bits; \
+ if (bit_offset >= 8) { \
+ bit_offset-=8; \
+ byte_offset++; \
+ } \
+ } while (0)
+
+#define PARAMETER_SIZE_CHECK(required_size) \
+ if (parameter->parameter_len < required_size) { \
+ mm_dbg (" cannot read address, need at least %u bytes (got %u)", \
+ required_size, \
+ parameter->parameter_len); \
+ return; \
+ }
+
+ /* Readability of digit mode and number mode (first 2 bits, i.e. first byte) */
+ PARAMETER_SIZE_CHECK (1);
+
+ /* Digit mode */
+ digit_mode = read_bits (&parameter->parameter_value[byte_offset], bit_offset, 1);
+ OFFSETS_UPDATE (1);
+ g_assert (digit_mode <= 1);
+ switch (digit_mode) {
+ case DIGIT_MODE_DTMF:
+ mm_dbg (" digit mode: dtmf");
+ break;
+ case DIGIT_MODE_ASCII:
+ mm_dbg (" digit mode: ascii");
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ /* Number mode */
+ number_mode = read_bits (&parameter->parameter_value[byte_offset], bit_offset, 1);
+ OFFSETS_UPDATE (1);
+ switch (number_mode) {
+ case NUMBER_MODE_DIGIT:
+ mm_dbg (" number mode: digit");
+ break;
+ case NUMBER_MODE_DATA_NETWORK_ADDRESS:
+ mm_dbg (" number mode: data network address");
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ /* Number type */
+ if (digit_mode == DIGIT_MODE_ASCII) {
+ /* No need for readability check, still in first byte always */
+ number_type = read_bits (&parameter->parameter_value[byte_offset], bit_offset, 3);
+ OFFSETS_UPDATE (3);
+ switch (number_type) {
+ case NUMBER_TYPE_UNKNOWN:
+ mm_dbg (" number type: unknown");
+ break;
+ case NUMBER_TYPE_INTERNATIONAL:
+ mm_dbg (" number type: international");
+ break;
+ case NUMBER_TYPE_NATIONAL:
+ mm_dbg (" number type: national");
+ break;
+ case NUMBER_TYPE_NETWORK_SPECIFIC:
+ mm_dbg (" number type: specific");
+ break;
+ case NUMBER_TYPE_SUBSCRIBER:
+ mm_dbg (" number type: subscriber");
+ break;
+ case NUMBER_TYPE_ABBREVIATED:
+ mm_dbg (" number type: abbreviated");
+ break;
+ default:
+ mm_dbg (" number type unknown (%u)", number_type);
+ break;
+ }
+ } else
+ number_type = 0xFF;
+
+ /* Numbering plan */
+ if (digit_mode == DIGIT_MODE_ASCII && number_mode == NUMBER_MODE_DIGIT) {
+ /* Readability of numbering plan; may go to second byte */
+ PARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + 4) / 8));
+ numbering_plan = read_bits (&parameter->parameter_value[byte_offset], bit_offset, 4);
+ OFFSETS_UPDATE (4);
+ switch (numbering_plan) {
+ case NUMBERING_PLAN_UNKNOWN:
+ mm_dbg (" numbering plan: unknown");
+ break;
+ case NUMBERING_PLAN_ISDN:
+ mm_dbg (" numbering plan: isdn");
+ break;
+ case NUMBERING_PLAN_DATA:
+ mm_dbg (" numbering plan: data");
+ break;
+ case NUMBERING_PLAN_TELEX:
+ mm_dbg (" numbering plan: telex");
+ break;
+ case NUMBERING_PLAN_PRIVATE:
+ mm_dbg (" numbering plan: private");
+ break;
+ default:
+ mm_dbg (" numbering plan unknown (%u)", numbering_plan);
+ break;
+ }
+ } else
+ numbering_plan = 0xFF;
+
+ /* Readability of num_fields; will go to third byte (((bit_offset + 8) / 8) == 1) */
+ PARAMETER_SIZE_CHECK (byte_offset + 2);
+ num_fields = read_bits (&parameter->parameter_value[byte_offset], bit_offset, 8);
+ OFFSETS_UPDATE (8);
+ mm_dbg (" num fields: %u", num_fields);
+
+ /* Address string */
+
+ if (digit_mode == DIGIT_MODE_DTMF) {
+ /* DTMF */
+ PARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + (num_fields * 4)) / 8));
+ number = g_malloc (num_fields + 1);
+ for (i = 0; i < num_fields; i++) {
+ number[i] = dtmf_to_ascii (read_bits (&parameter->parameter_value[byte_offset], bit_offset, 4));
+ OFFSETS_UPDATE (4);
+ }
+ number[i] = '\0';
+ } else if (number_mode == NUMBER_MODE_DIGIT) {
+ /* ASCII
+ * TODO: should we expose numbering plan and number type? */
+ PARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + (num_fields * 8)) / 8));
+ number = g_malloc (num_fields + 1);
+ for (i = 0; i < num_fields; i++) {
+ number[i] = read_bits (&parameter->parameter_value[byte_offset], bit_offset, 8);
+ OFFSETS_UPDATE (8);
+ }
+ number[i] = '\0';
+ } else if (number_type == DATA_NETWORK_ADDRESS_TYPE_INTERNET_EMAIL_ADDRESS) {
+ /* Internet e-mail address (ASCII) */
+ PARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + (num_fields * 8)) / 8));
+ number = g_malloc (num_fields + 1);
+ for (i = 0; i < num_fields; i++) {
+ number[i] = read_bits (&parameter->parameter_value[byte_offset], bit_offset, 8);
+ OFFSETS_UPDATE (8);
+ }
+ number[i] = '\0';
+ } else if (number_type == DATA_NETWORK_ADDRESS_TYPE_INTERNET_PROTOCOL) {
+ GString *str;
+
+ /* Binary data network address (most significant first)
+ * For now, just print the hex string (e.g. FF:01...) */
+ PARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + (num_fields * 8)) / 8));
+ str = g_string_sized_new (num_fields * 2);
+ for (i = 0; i < num_fields; i++) {
+ g_string_append_printf (str, "%.2X", read_bits (&parameter->parameter_value[byte_offset], bit_offset, 8));
+ OFFSETS_UPDATE (8);
+ }
+ number = g_string_free (str, FALSE);
+ } else
+ mm_dbg (" data network address number type unknown (%u)", number_type);
+
+ mm_dbg (" address: %s", number);
+
+ mm_sms_part_set_number (sms_part, number);
+ g_free (number);
+
+#undef OFFSETS_UPDATE
+#undef PARAMETER_SIZE_CHECK
+}
+
+static void
+read_bearer_reply_option (MMSmsPart *sms_part,
+ const struct Parameter *parameter)
+{
+ guint8 sequence;
+
+ g_assert (parameter->parameter_id == PARAMETER_ID_BEARER_REPLY_OPTION);
+
+ if (parameter->parameter_len != 1) {
+ mm_dbg (" invalid bearer reply option length found (%u != 1): ignoring",
+ parameter->parameter_len);
+ return;
+ }
+
+ sequence = read_bits (&parameter->parameter_value[0], 0, 6);
+ mm_dbg (" sequence: %u", sequence);
+
+ mm_sms_part_set_message_reference (sms_part, sequence);
+}
+
+static void
+read_cause_codes (MMSmsPart *sms_part,
+ const struct Parameter *parameter)
+{
+ guint8 sequence;
+ guint8 error_class;
+ guint8 cause_code;
+ MMSmsDeliveryState delivery_state;
+
+ g_assert (parameter->parameter_id == PARAMETER_ID_BEARER_REPLY_OPTION);
+
+ if (parameter->parameter_len != 1 && parameter->parameter_len != 2) {
+ mm_dbg (" invalid cause codes length found (%u): ignoring",
+ parameter->parameter_len);
+ return;
+ }
+
+ sequence = read_bits (&parameter->parameter_value[0], 0, 6);
+ mm_dbg (" sequence: %u", sequence);
+
+ error_class = read_bits (&parameter->parameter_value[0], 6, 2);
+ mm_dbg (" error class: %u", error_class);
+
+ if (error_class != ERROR_CLASS_NO_ERROR) {
+ if (parameter->parameter_len != 2) {
+ mm_dbg (" invalid cause codes length found (%u != 2): ignoring",
+ parameter->parameter_len);
+ return;
+ }
+ cause_code = parameter->parameter_value[1];
+ mm_dbg (" cause code: %u", cause_code);
+ } else
+ cause_code = 0;
+
+ delivery_state = cause_code_to_delivery_state (error_class, cause_code);
+ mm_dbg (" delivery state: %s", mm_sms_delivery_state_get_string (delivery_state));
+
+ mm_sms_part_set_message_reference (sms_part, sequence);
+ mm_sms_part_set_delivery_state (sms_part, delivery_state);
+}
+
+static void
+read_bearer_data_message_identifier (MMSmsPart *sms_part,
+ const struct Parameter *subparameter)
+{
+ guint8 message_type;
+ guint16 message_id;
+ guint8 header_ind;
+
+ g_assert (subparameter->parameter_id == SUBPARAMETER_ID_MESSAGE_ID);
+
+ if (subparameter->parameter_len != 3) {
+ mm_dbg (" invalid message identifier length found (%u): ignoring",
+ subparameter->parameter_len);
+ return;
+ }
+
+ message_type = read_bits (&subparameter->parameter_value[0], 0, 4);
+ switch (message_type) {
+ case TELESERVICE_MESSAGE_TYPE_UNKNOWN:
+ mm_dbg (" message type: unknown");
+ break;
+ case TELESERVICE_MESSAGE_TYPE_DELIVER:
+ mm_dbg (" message type: deliver");
+ mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_CDMA_DELIVER);
+ break;
+ case TELESERVICE_MESSAGE_TYPE_SUBMIT:
+ mm_dbg (" message type: submit");
+ mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_CDMA_SUBMIT);
+ break;
+ case TELESERVICE_MESSAGE_TYPE_CANCELLATION:
+ mm_dbg (" message type: cancellation");
+ mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_CDMA_CANCELLATION);
+ break;
+ case TELESERVICE_MESSAGE_TYPE_DELIVERY_ACKNOWLEDGEMENT:
+ mm_dbg (" message type: delivery acknowledgement");
+ mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_CDMA_DELIVERY_ACKNOWLEDGEMENT);
+ break;
+ case TELESERVICE_MESSAGE_TYPE_USER_ACKNOWLEDGEMENT:
+ mm_dbg (" message type: user acknowledgement");
+ mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_CDMA_USER_ACKNOWLEDGEMENT);
+ break;
+ case TELESERVICE_MESSAGE_TYPE_READ_ACKNOWLEDGEMENT:
+ mm_dbg (" message type: read acknowledgement");
+ mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_CDMA_READ_ACKNOWLEDGEMENT);
+ break;
+ default:
+ mm_dbg (" message type unknown (%u)", message_type);
+ break;
+ }
+
+ message_id = ((read_bits (&subparameter->parameter_value[0], 4, 8) << 8) |
+ (read_bits (&subparameter->parameter_value[1], 4, 8)));
+ message_id = GUINT16_FROM_BE (message_id);
+ mm_dbg (" message id: %u", (guint) message_id);
+
+ header_ind = read_bits (&subparameter->parameter_value[2], 4, 1);
+ mm_dbg (" header indicator: %u", header_ind);
+}
+
+static void
+read_bearer_data_user_data (MMSmsPart *sms_part,
+ const struct Parameter *subparameter)
+{
+ guint8 message_encoding;
+ guint8 message_type = 0;
+ guint8 num_fields;
+ guint byte_offset = 0;
+ guint bit_offset = 0;
+
+#define OFFSETS_UPDATE(n_bits) do { \
+ bit_offset += n_bits; \
+ if (bit_offset >= 8) { \
+ bit_offset-=8; \
+ byte_offset++; \
+ } \
+ } while (0)
+
+#define SUBPARAMETER_SIZE_CHECK(required_size) \
+ if (subparameter->parameter_len < required_size) { \
+ mm_dbg (" cannot read user data, need at least %u bytes (got %u)", \
+ required_size, \
+ subparameter->parameter_len); \
+ return; \
+ }
+
+ g_assert (subparameter->parameter_id == SUBPARAMETER_ID_USER_DATA);
+
+ /* Message encoding */
+ SUBPARAMETER_SIZE_CHECK (1);
+ message_encoding = read_bits (&subparameter->parameter_value[byte_offset], bit_offset, 5);
+ OFFSETS_UPDATE (5);
+ mm_dbg (" message encoding: %s", encoding_to_string (message_encoding));
+
+ /* Message type, only if extended protocol message */
+ if (message_encoding == ENCODING_EXTENDED_PROTOCOL_MESSAGE) {
+ SUBPARAMETER_SIZE_CHECK (2);
+ message_type = read_bits (&subparameter->parameter_value[byte_offset], bit_offset, 8);
+ OFFSETS_UPDATE (8);
+ mm_dbg (" message type: %u", message_type);
+ }
+
+ /* Number of fields */
+ SUBPARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + 8) / 8));
+ num_fields = read_bits (&subparameter->parameter_value[byte_offset], bit_offset, 8);
+ OFFSETS_UPDATE (8);
+ mm_dbg (" num fields: %u", num_fields);
+
+ /* Now, process actual text or data */
+ switch (message_encoding) {
+ case ENCODING_OCTET: {
+ GByteArray *data;
+ guint i;
+
+ SUBPARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + (num_fields * 8)) / 8));
+
+ data = g_byte_array_sized_new (num_fields);
+ g_byte_array_set_size (data, num_fields);
+ for (i = 0; i < num_fields; i++) {
+ data->data[i] = read_bits (&subparameter->parameter_value[byte_offset], bit_offset, 8);
+ OFFSETS_UPDATE (8);
+ }
+
+ mm_dbg (" data: (%u bytes)", num_fields);
+ mm_sms_part_take_data (sms_part, data);
+ break;
+ }
+
+ case ENCODING_ASCII_7BIT: {
+ gchar *text;
+ guint i;
+
+ SUBPARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + (num_fields * 7)) / 8));
+
+ text = g_malloc (num_fields + 1);
+ for (i = 0; i < num_fields; i++) {
+ text[i] = read_bits (&subparameter->parameter_value[byte_offset], bit_offset, 7);
+ OFFSETS_UPDATE (7);
+ }
+ text[i] = '\0';
+
+ mm_dbg (" text: '%s'", text);
+ mm_sms_part_take_text (sms_part, text);
+ break;
+ }
+
+ case ENCODING_LATIN: {
+ gchar *latin;
+ gchar *text;
+ guint i;
+
+ SUBPARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + (num_fields * 8)) / 8));
+
+ latin = g_malloc (num_fields + 1);
+ for (i = 0; i < num_fields; i++) {
+ latin[i] = read_bits (&subparameter->parameter_value[byte_offset], bit_offset, 8);
+ OFFSETS_UPDATE (8);
+ }
+ latin[i] = '\0';
+
+ text = g_convert (latin, -1, "UTF-8", "ISO−8859−1", NULL, NULL, NULL);
+ if (!text) {
+ mm_dbg (" text/data: ignored (latin to UTF-8 conversion error)");
+ } else {
+ mm_dbg (" text: '%s'", text);
+ mm_sms_part_take_text (sms_part, text);
+ }
+
+ g_free (latin);
+ break;
+ }
+
+ case ENCODING_UNICODE: {
+ gchar *utf16;
+ gchar *text;
+ guint i;
+ guint num_bytes;
+
+ /* 2 bytes per field! */
+ num_bytes = num_fields * 2;
+
+ SUBPARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + (num_bytes * 8)) / 8));
+
+ utf16 = g_malloc (num_bytes);
+ for (i = 0; i < num_bytes; i++) {
+ utf16[i] = read_bits (&subparameter->parameter_value[byte_offset], bit_offset, 8);
+ OFFSETS_UPDATE (8);
+ }
+
+ text = g_convert (utf16, num_bytes, "UTF-8", "UCS-2BE", NULL, NULL, NULL);
+ if (!text) {
+ mm_dbg (" text/data: ignored (UTF-16 to UTF-8 conversion error)");
+ } else {
+ mm_dbg (" text: '%s'", text);
+ mm_sms_part_take_text (sms_part, text);
+ }
+
+ g_free (utf16);
+ break;
+ }
+
+ default:
+ mm_dbg (" text/data: ignored (unsupported encoding)");
+ }
+
+#undef OFFSETS_UPDATE
+#undef SUBPARAMETER_SIZE_CHECK
+}
+
+static void
+read_bearer_data (MMSmsPart *sms_part,
+ const struct Parameter *parameter)
+{
+ guint offset;
+
+#define PARAMETER_SIZE_CHECK(required_size) \
+ if (parameter->parameter_len < required_size) { \
+ mm_dbg (" cannot read bearer data, need at least %u bytes (got %u)", \
+ required_size, \
+ parameter->parameter_len); \
+ return; \
+ }
+
+ offset = 0;
+ while (offset < parameter->parameter_len) {
+ const struct Parameter *subparameter;
+
+ PARAMETER_SIZE_CHECK (offset + 2);
+ subparameter = (const struct Parameter *)&parameter->parameter_value[offset];
+ offset += 2;
+
+ PARAMETER_SIZE_CHECK (offset + subparameter->parameter_len);
+ offset += subparameter->parameter_len;
+
+ switch (subparameter->parameter_id) {
+ case SUBPARAMETER_ID_MESSAGE_ID:
+ mm_dbg (" reading message ID...");
+ read_bearer_data_message_identifier (sms_part, subparameter);
+ break;
+ case SUBPARAMETER_ID_USER_DATA:
+ mm_dbg (" reading user data...");
+ read_bearer_data_user_data (sms_part, subparameter);
+ break;
+ case SUBPARAMETER_ID_USER_RESPONSE_CODE:
+ mm_dbg (" skipping user response code...");
+ break;
+ case SUBPARAMETER_ID_MESSAGE_CENTER_TIME_STAMP:
+ mm_dbg (" skipping message center timestamp...");
+ break;
+ case SUBPARAMETER_ID_VALIDITY_PERIOD_ABSOLUTE:
+ mm_dbg (" skipping absolute validity period...");
+ break;
+ case SUBPARAMETER_ID_VALIDITY_PERIOD_RELATIVE:
+ mm_dbg (" skipping relative validity period...");
+ break;
+ case SUBPARAMETER_ID_DEFERRED_DELIVERY_TIME_ABSOLUTE:
+ mm_dbg (" skipping absolute deferred delivery time...");
+ break;
+ case SUBPARAMETER_ID_DEFERRED_DELIVERY_TIME_RELATIVE:
+ mm_dbg (" skipping relative deferred delivery time...");
+ break;
+ case SUBPARAMETER_ID_PRIORITY_INDICATOR:
+ mm_dbg (" skipping priority indicator...");
+ break;
+ case SUBPARAMETER_ID_PRIVACY_INDICATOR:
+ mm_dbg (" skipping privacy indicator...");
+ break;
+ case SUBPARAMETER_ID_REPLY_OPTION:
+ mm_dbg (" skipping reply option...");
+ break;
+ case SUBPARAMETER_ID_NUMBER_OF_MESSAGES:
+ mm_dbg (" skipping number of messages...");
+ break;
+ case SUBPARAMETER_ID_ALERT_ON_MESSAGE_DELIVERY:
+ mm_dbg (" skipping alert on message delivery...");
+ break;
+ case SUBPARAMETER_ID_LANGUAGE_INDICATOR:
+ mm_dbg (" skipping language indicator...");
+ break;
+ case SUBPARAMETER_ID_CALL_BACK_NUMBER:
+ mm_dbg (" skipping call back number...");
+ break;
+ case SUBPARAMETER_ID_MESSAGE_DISPLAY_MODE:
+ mm_dbg (" skipping message display mode...");
+ break;
+ case SUBPARAMETER_ID_MULTIPLE_ENCODING_USER_DATA:
+ mm_dbg (" skipping multiple encoding user data...");
+ break;
+ case SUBPARAMETER_ID_MESSAGE_DEPOSIT_INDEX:
+ mm_dbg (" skipping message deposit index...");
+ break;
+ case SUBPARAMETER_ID_SERVICE_CATEGORY_PROGRAM_DATA:
+ mm_dbg (" skipping service category program data...");
+ break;
+ case SUBPARAMETER_ID_SERVICE_CATEGORY_PROGRAM_RESULT:
+ mm_dbg (" skipping service category program result...");
+ break;
+ case SUBPARAMETER_ID_MESSAGE_STATUS:
+ mm_dbg (" skipping message status...");
+ break;
+ case SUBPARAMETER_ID_TP_FAILURE_CAUSE:
+ mm_dbg (" skipping TP failure case...");
+ break;
+ case SUBPARAMETER_ID_ENHANCED_VMN:
+ mm_dbg (" skipping enhanced vmn...");
+ break;
+ case SUBPARAMETER_ID_ENHANCED_VMN_ACK:
+ mm_dbg (" skipping enhanced vmn ack...");
+ break;
+ default:
+ mm_dbg (" unknown subparameter found: '%u' (ignoring)",
+ subparameter->parameter_id);
+ break;
+ }
+ }
+
+#undef PARAMETER_SIZE_CHECK
+}
+
+MMSmsPart *
+mm_sms_part_cdma_new_from_binary_pdu (guint index,
+ const guint8 *pdu,
+ gsize pdu_len,
+ GError **error)
+{
+ MMSmsPart *sms_part;
+ guint offset;
+ guint message_type;
+
+ /* Create the new MMSmsPart */
+ sms_part = mm_sms_part_new (index, MM_SMS_PDU_TYPE_UNKNOWN);
+
+ if (index != SMS_PART_INVALID_INDEX)
+ mm_dbg ("Parsing CDMA PDU (%u)...", index);
+ else
+ mm_dbg ("Parsing CDMA PDU...");
+
+#define PDU_SIZE_CHECK(required_size, check_descr_str) \
+ if (pdu_len < required_size) { \
+ g_set_error (error, \
+ MM_CORE_ERROR, \
+ MM_CORE_ERROR_FAILED, \
+ "CDMA PDU too short, %s: %" G_GSIZE_FORMAT " < %u", \
+ check_descr_str, \
+ pdu_len, \
+ required_size); \
+ mm_sms_part_free (sms_part); \
+ return NULL; \
+ }
+
+ offset = 0;
+
+ /* First byte: SMS message type */
+ PDU_SIZE_CHECK (offset + 1, "cannot read SMS message type");
+ message_type = pdu[offset++];
+ switch (message_type) {
+ case MESSAGE_TYPE_POINT_TO_POINT:
+ case MESSAGE_TYPE_BROADCAST:
+ case MESSAGE_TYPE_ACKNOWLEDGE:
+ break;
+ default:
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Invalid SMS message type (%u)",
+ message_type);
+ mm_sms_part_free (sms_part);
+ return NULL;
+ }
+
+ /* Now walk parameters one by one */
+ while (offset < pdu_len) {
+ const struct Parameter *parameter;
+
+ PDU_SIZE_CHECK (offset + 2, "cannot read parameter header");
+ parameter = (const struct Parameter *)&pdu[offset];
+ offset += 2;
+
+ PDU_SIZE_CHECK (offset + parameter->parameter_len, "cannot read parameter value");
+ offset += parameter->parameter_len;
+
+ switch (parameter->parameter_id) {
+ case PARAMETER_ID_TELESERVICE_ID:
+ mm_dbg (" reading teleservice ID...");
+ read_teleservice_id (sms_part, parameter);
+ break;
+ case PARAMETER_ID_SERVICE_CATEGORY:
+ mm_dbg (" reading service category...");
+ read_service_category (sms_part, parameter);
+ break;
+ case PARAMETER_ID_ORIGINATING_ADDRESS:
+ mm_dbg (" reading originating address...");
+ if (mm_sms_part_get_number (sms_part))
+ mm_dbg (" cannot read originating address; an address field was already read");
+ else
+ read_address (sms_part, parameter);
+ break;
+ case PARAMETER_ID_ORIGINATING_SUBADDRESS:
+ mm_dbg (" skipping originating subaddress...");
+ break;
+ case PARAMETER_ID_DESTINATION_ADDRESS:
+ mm_dbg (" reading destination address...");
+ if (mm_sms_part_get_number (sms_part))
+ mm_dbg (" cannot read destination address; an address field was already read");
+ else
+ read_address (sms_part, parameter);
+ break;
+ case PARAMETER_ID_DESTINATION_SUBADDRESS:
+ mm_dbg (" skipping destination subaddress...");
+ break;
+ case PARAMETER_ID_BEARER_REPLY_OPTION:
+ mm_dbg (" reading bearer reply option...");
+ read_bearer_reply_option (sms_part, parameter);
+ break;
+ case PARAMETER_ID_CAUSE_CODES:
+ mm_dbg (" reading cause codes...");
+ read_cause_codes (sms_part, parameter);
+ break;
+ case PARAMETER_ID_BEARER_DATA:
+ mm_dbg (" reading bearer data...");
+ read_bearer_data (sms_part, parameter);
+ break;
+ default:
+ mm_dbg (" unknown parameter found: '%u' (ignoring)",
+ parameter->parameter_id);
+ break;
+ }
+ }
+
+ /* Check mandatory parameters */
+ switch (message_type) {
+ case MESSAGE_TYPE_POINT_TO_POINT:
+ if (mm_sms_part_get_cdma_teleservice_id (sms_part) == MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN)
+ mm_dbg (" mandatory parameter missing: teleservice ID not found or invalid in point-to-point message");
+ break;
+ case MESSAGE_TYPE_BROADCAST:
+ if (mm_sms_part_get_cdma_service_category (sms_part) == MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN)
+ mm_dbg (" mandatory parameter missing: service category not found or invalid in broadcast message");
+ break;
+ case MESSAGE_TYPE_ACKNOWLEDGE:
+ if (mm_sms_part_get_message_reference (sms_part) == 0)
+ mm_dbg (" mandatory parameter missing: cause codes not found or invalid in acknowledge message");
+ break;
+ }
+
+#undef PDU_SIZE_CHECK
+
+ return sms_part;
+}
+
+/*****************************************************************************/
+/* Write bits; o_bits < 8; n_bits <= 8
+ *
+ * Byte 0 Byte 1
+ * [7|6|5|4|3|2|1|0] [7|6|5|4|3|2|1|0]
+ *
+ * o_bits+n_bits <= 16
+ *
+ * NOTE! The bits being set should be 0 initially.
+ */
+static void
+write_bits (guint8 *bytes,
+ guint8 o_bits,
+ guint8 n_bits,
+ guint8 bits)
+{
+ guint8 bits_in_first;
+ guint8 bits_in_second;
+
+ g_assert (o_bits < 8);
+ g_assert (n_bits <= 8);
+ g_assert (o_bits + n_bits <= 16);
+
+ /* Write only in the first byte */
+ if (o_bits + n_bits <= 8) {
+ bytes[0] |= (bits & ((1 << n_bits) - 1)) << (8 - o_bits - n_bits);
+ return;
+ }
+
+ /* Write (8 - o_bits) in the first byte and (n_bits - (8 - o_bits)) in the second byte */
+ bits_in_first = 8 - o_bits;
+ bits_in_second = n_bits - bits_in_first;
+
+ write_bits (&bytes[0], o_bits, bits_in_first, (bits >> bits_in_second));
+ write_bits (&bytes[1], 0, bits_in_second, bits);
+}
+
+/*****************************************************************************/
+
+static guint8
+dtmf_from_ascii (guint8 ascii)
+{
+ if (ascii >= '1' && ascii <= '9')
+ return ascii - '0';
+ if (ascii == '0')
+ return 10;
+ if (ascii == '*')
+ return 11;
+ if (ascii == '#')
+ return 12;
+
+ mm_dbg (" invalid ascii digit in dtmf conversion: %c", ascii);
+ return 0;
+}
+
+static gboolean
+write_teleservice_id (MMSmsPart *part,
+ guint8 *pdu,
+ guint *absolute_offset,
+ GError **error)
+{
+ guint16 aux16;
+
+ mm_dbg (" writing teleservice ID...");
+
+ if (mm_sms_part_get_cdma_teleservice_id (part) != MM_SMS_CDMA_TELESERVICE_ID_WMT) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Teleservice '%s' not supported",
+ mm_sms_cdma_teleservice_id_get_string (
+ mm_sms_part_get_cdma_teleservice_id (part)));
+ return FALSE;
+ }
+
+ mm_dbg (" teleservice ID: %s (%u)",
+ mm_sms_cdma_teleservice_id_get_string (MM_SMS_CDMA_TELESERVICE_ID_WMT),
+ MM_SMS_CDMA_TELESERVICE_ID_WMT);
+
+ /* Teleservice ID: WMT always */
+ pdu[0] = PARAMETER_ID_TELESERVICE_ID;
+ pdu[1] = 2; /* parameter_len, always 2 */
+ aux16 = GUINT16_TO_BE (MM_SMS_CDMA_TELESERVICE_ID_WMT);
+ memcpy (&pdu[2], &aux16, 2);
+
+ *absolute_offset += 4;
+ return TRUE;
+}
+
+static gboolean
+write_destination_address (MMSmsPart *part,
+ guint8 *pdu,
+ guint *absolute_offset,
+ GError **error)
+{
+ const gchar *number;
+ guint bit_offset;
+ guint byte_offset;
+ guint n_digits;
+ guint i;
+
+ mm_dbg (" writing destination address...");
+
+#define OFFSETS_UPDATE(n_bits) do { \
+ bit_offset += n_bits; \
+ if (bit_offset >= 8) { \
+ bit_offset-=8; \
+ byte_offset++; \
+ } \
+ } while (0)
+
+ number = mm_sms_part_get_number (part);
+ n_digits = strlen (number);
+
+ pdu[0] = PARAMETER_ID_DESTINATION_ADDRESS;
+ /* Write parameter length at the end */
+
+ byte_offset = 2;
+ bit_offset = 0;
+
+ /* Digit mode: DTMF always */
+ mm_dbg (" digit mode: dtmf");
+ write_bits (&pdu[byte_offset], bit_offset, 1, DIGIT_MODE_DTMF);
+ OFFSETS_UPDATE (1);
+
+ /* Number mode: DIGIT always */
+ mm_dbg (" number mode: digit");
+ write_bits (&pdu[byte_offset], bit_offset, 1, NUMBER_MODE_DIGIT);
+ OFFSETS_UPDATE (1);
+
+ /* Number type and numbering plan only needed in ASCII digit mode, so skip */
+
+ /* Number of fields */
+ if (n_digits > 256) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Number too long (max 256 digits, %u given)",
+ n_digits);
+ return FALSE;
+ }
+ mm_dbg (" num fields: %u", n_digits);
+ write_bits (&pdu[byte_offset], bit_offset, 8, n_digits);
+ OFFSETS_UPDATE (8);
+
+ /* Actual DTMF encoded number */
+ mm_dbg (" address: %s", number);
+ for (i = 0; i < n_digits; i++) {
+ guint8 dtmf;
+
+ dtmf = dtmf_from_ascii (number[i]);
+ if (!dtmf) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Unsupported character in number: '%c'. Cannot convert to DTMF",
+ number[i]);
+ return FALSE;
+ }
+ write_bits (&pdu[byte_offset], bit_offset, 4, dtmf);
+ OFFSETS_UPDATE (4);
+ }
+
+#undef OFFSETS_UPDATE
+
+ /* Write parameter length (remove header length to offset) */
+ byte_offset += !!bit_offset - 2;
+ if (byte_offset > 256) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Number too long (max 256 bytes, %u given)",
+ byte_offset);
+ return FALSE;
+ }
+ pdu[1] = byte_offset;
+
+ *absolute_offset += (2 + pdu[1]);
+ return TRUE;
+}
+
+static gboolean
+write_bearer_data_message_identifier (MMSmsPart *part,
+ guint8 *pdu,
+ guint *parameter_offset,
+ GError **error)
+{
+ pdu[0] = SUBPARAMETER_ID_MESSAGE_ID;
+ pdu[1] = 3; /* subparameter_len, always 3 */
+
+ mm_dbg (" writing message identifier: submit");
+
+ /* Message type */
+ write_bits (&pdu[2], 0, 4, TELESERVICE_MESSAGE_TYPE_SUBMIT);
+
+ /* Skip adding a message id; assume it's filled in by device */
+
+ /* And no need for a header ind value, always false */
+
+ *parameter_offset += 5;
+ return TRUE;
+}
+
+static void
+decide_best_encoding (const gchar *text,
+ GByteArray **out,
+ guint *num_fields,
+ guint *num_bits_per_field,
+ Encoding *encoding)
+{
+ guint latin_unsupported = 0;
+ guint ascii_unsupported = 0;
+ guint i;
+ guint len;
+
+ len = strlen (text);
+
+ /* Check if we can do ASCII-7 */
+ for (i = 0; i < len; i++) {
+ if (text[i] & 0x80) {
+ ascii_unsupported++;
+ break;
+ }
+ }
+
+ /* If ASCII-7 already supported, done we are */
+ if (!ascii_unsupported) {
+ *out = g_byte_array_sized_new (len);
+ g_byte_array_append (*out, (const guint8 *)text, len);
+ *num_fields = len;
+ *num_bits_per_field = 7;
+ *encoding = ENCODING_ASCII_7BIT;
+ return;
+ }
+
+ /* Check if we can do Latin encoding */
+ mm_charset_get_encoded_len (text,
+ MM_MODEM_CHARSET_8859_1,
+ &latin_unsupported);
+ if (!latin_unsupported) {
+ *out = g_byte_array_sized_new (len);
+ mm_modem_charset_byte_array_append (*out,
+ text,
+ FALSE,
+ MM_MODEM_CHARSET_8859_1);
+ *num_fields = (*out)->len;
+ *num_bits_per_field = 8;
+ *encoding = ENCODING_LATIN;
+ return;
+ }
+
+ /* If no Latin and no ASCII, default to UTF-16 */
+ *out = g_byte_array_sized_new (len * 2);
+ mm_modem_charset_byte_array_append (*out,
+ text,
+ FALSE,
+ MM_MODEM_CHARSET_UCS2);
+ *num_fields = (*out)->len / 2;
+ *num_bits_per_field = 16;
+ *encoding = ENCODING_UNICODE;
+}
+
+static gboolean
+write_bearer_data_user_data (MMSmsPart *part,
+ guint8 *pdu,
+ guint *parameter_offset,
+ GError **error)
+{
+ const gchar *text;
+ const GByteArray *data;
+ guint bit_offset = 0;
+ guint byte_offset = 0;
+ guint num_fields;
+ guint num_bits_per_field;
+ guint i;
+ Encoding encoding;
+ GByteArray *converted = NULL;
+ const GByteArray *aux;
+ guint num_bits_per_iter;
+
+ mm_dbg (" writing user data...");
+
+#define OFFSETS_UPDATE(n_bits) do { \
+ bit_offset += n_bits; \
+ if (bit_offset >= 8) { \
+ bit_offset-=8; \
+ byte_offset++; \
+ } \
+ } while (0)
+
+ text = mm_sms_part_get_text (part);
+ data = mm_sms_part_get_data (part);
+ g_assert (text || data);
+ g_assert (!(!text && !data));
+
+ pdu[0] = SUBPARAMETER_ID_USER_DATA;
+ /* Write parameter length at the end */
+ byte_offset = 2;
+ bit_offset = 0;
+
+ /* Text or Data */
+ if (text) {
+ decide_best_encoding (text,
+ &converted,
+ &num_fields,
+ &num_bits_per_field,
+ &encoding);
+ aux = (const GByteArray *)converted;
+ } else {
+ aux = data;
+ num_fields = data->len;
+ num_bits_per_field = 8;
+ encoding = ENCODING_OCTET;
+ }
+
+ /* Message encoding*/
+ mm_dbg (" message encoding: %s", encoding_to_string (encoding));
+ write_bits (&pdu[byte_offset], bit_offset, 5, encoding);
+ OFFSETS_UPDATE (5);
+
+ /* Number of fields */
+ if (num_fields > 256) {
+ if (converted)
+ g_byte_array_unref (converted);
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Data too long (max 256 fields, %u given)",
+ num_fields);
+ return FALSE;
+ }
+ mm_dbg (" num fields: %u", num_fields);
+ write_bits (&pdu[byte_offset], bit_offset, 8, num_fields);
+ OFFSETS_UPDATE (8);
+
+ /* For ASCII-7, write 7 bits in each iteration; for the remaining ones
+ * go byte per byte */
+ if (text)
+ mm_dbg (" text: '%s'", text);
+ else
+ mm_dbg (" data: (%u bytes)", num_fields);
+ num_bits_per_iter = num_bits_per_field < 8 ? num_bits_per_field : 8;
+ for (i = 0; i < aux->len; i++) {
+ write_bits (&pdu[byte_offset], bit_offset, num_bits_per_iter, aux->data[i]);
+ OFFSETS_UPDATE (num_bits_per_iter);
+ }
+
+ if (converted)
+ g_byte_array_unref (converted);
+
+#undef OFFSETS_UPDATE
+
+ /* Write subparameter length (remove header length to offset) */
+ byte_offset += !!bit_offset - 2;
+ if (byte_offset > 256) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Data or Text too long (max 256 bytes, %u given)",
+ byte_offset);
+ return FALSE;
+ }
+ pdu[1] = byte_offset;
+
+ *parameter_offset += (2 + pdu[1]);
+ return TRUE;
+}
+
+static gboolean
+write_bearer_data (MMSmsPart *part,
+ guint8 *pdu,
+ guint *absolute_offset,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ guint offset = 0;
+
+ mm_dbg (" writing bearer data...");
+
+ pdu[0] = PARAMETER_ID_BEARER_DATA;
+ /* Write parameter length at the end */
+
+ offset = 2;
+ if (!write_bearer_data_message_identifier (part, &pdu[offset], &offset, &inner_error))
+ mm_dbg ("Error writing message identifier: %s", inner_error->message);
+ else if (!write_bearer_data_user_data (part, &pdu[offset], &offset, &inner_error))
+ mm_dbg ("Error writing user data: %s", inner_error->message);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ g_prefix_error (error, "Error writing bearer data: ");
+ return FALSE;
+ }
+
+ /* Write parameter length (remove header length to offset) */
+ offset -= 2;
+ if (offset > 256) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Bearer data too long (max 256 bytes, %u given)",
+ offset);
+ return FALSE;
+ }
+ pdu[1] = offset;
+
+ *absolute_offset += (2 + pdu[1]);
+ return TRUE;
+}
+
+guint8 *
+mm_sms_part_cdma_get_submit_pdu (MMSmsPart *part,
+ guint *out_pdulen,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ guint offset = 0;
+ guint8 *pdu;
+
+ g_return_val_if_fail (mm_sms_part_get_number (part) != NULL, NULL);
+ g_return_val_if_fail (mm_sms_part_get_text (part) != NULL || mm_sms_part_get_data (part) != NULL, NULL);
+
+ if (mm_sms_part_get_pdu_type (part) != MM_SMS_PDU_TYPE_CDMA_SUBMIT) {
+ g_set_error (error,
+ MM_MESSAGE_ERROR,
+ MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
+ "Invalid PDU type to generate a 'submit' PDU: '%s'",
+ mm_sms_pdu_type_get_string (mm_sms_part_get_pdu_type (part)));
+ return NULL;
+ }
+
+ mm_dbg ("Creating PDU for part...");
+
+ /* Current max size estimations:
+ * Message type: 1 byte
+ * Teleservice ID: 5 bytes
+ * Destination address: 2 + 256 bytes
+ * Bearer data: 2 + 256 bytes
+ */
+ pdu = g_malloc0 (1024);
+
+ /* First byte: SMS message type */
+ pdu[offset++] = MESSAGE_TYPE_POINT_TO_POINT;
+
+ if (!write_teleservice_id (part, &pdu[offset], &offset, &inner_error))
+ mm_dbg ("Error writing Teleservice ID: %s", inner_error->message);
+ else if (!write_destination_address (part, &pdu[offset], &offset, &inner_error))
+ mm_dbg ("Error writing destination address: %s", inner_error->message);
+ else if (!write_bearer_data (part, &pdu[offset], &offset, &inner_error))
+ mm_dbg ("Error writing bearer data: %s", inner_error->message);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ g_prefix_error (error, "Cannot create CDMA SMS part: ");
+ g_free (pdu);
+ return NULL;
+ }
+
+ *out_pdulen = offset;
+ return pdu;
+}
diff --git a/src/mm-sms-part-cdma.h b/src/mm-sms-part-cdma.h
new file mode 100644
index 0000000..14c2c1d
--- /dev/null
+++ b/src/mm-sms-part-cdma.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2013 Google, Inc.
+ */
+
+#ifndef MM_SMS_PART_CDMA_H
+#define MM_SMS_PART_CDMA_H
+
+#include <glib.h>
+#include <ModemManager-enums.h>
+
+#include "mm-sms-part.h"
+
+MMSmsPart *mm_sms_part_cdma_new_from_pdu (guint index,
+ const gchar *hexpdu,
+ GError **error);
+
+MMSmsPart *mm_sms_part_cdma_new_from_binary_pdu (guint index,
+ const guint8 *pdu,
+ gsize pdu_len,
+ GError **error);
+
+guint8 *mm_sms_part_cdma_get_submit_pdu (MMSmsPart *part,
+ guint *out_pdulen,
+ GError **error);
+
+#endif /* MM_SMS_PART_CDMA_H */
diff --git a/src/mm-sms-part.c b/src/mm-sms-part.c
index 77fa3b8..9aafcc3 100644
--- a/src/mm-sms-part.c
+++ b/src/mm-sms-part.c
@@ -27,294 +27,6 @@
#include "mm-charsets.h"
#include "mm-log.h"
-#define PDU_SIZE 200
-
-#define SMS_TP_MTI_MASK 0x03
-#define SMS_TP_MTI_SMS_DELIVER 0x00
-#define SMS_TP_MTI_SMS_SUBMIT 0x01
-#define SMS_TP_MTI_SMS_STATUS_REPORT 0x02
-
-#define SMS_NUMBER_TYPE_MASK 0x70
-#define SMS_NUMBER_TYPE_UNKNOWN 0x00
-#define SMS_NUMBER_TYPE_INTL 0x10
-#define SMS_NUMBER_TYPE_ALPHA 0x50
-
-#define SMS_NUMBER_PLAN_MASK 0x0f
-#define SMS_NUMBER_PLAN_TELEPHONE 0x01
-
-#define SMS_TP_MMS 0x04
-#define SMS_TP_SRI 0x20
-#define SMS_TP_UDHI 0x40
-#define SMS_TP_RP 0x80
-
-#define SMS_DCS_CODING_MASK 0xec
-#define SMS_DCS_CODING_DEFAULT 0x00
-#define SMS_DCS_CODING_8BIT 0x04
-#define SMS_DCS_CODING_UCS2 0x08
-
-#define SMS_DCS_CLASS_VALID 0x10
-#define SMS_DCS_CLASS_MASK 0x03
-
-#define SMS_TIMESTAMP_LEN 7
-#define SMS_MIN_PDU_LEN (7 + SMS_TIMESTAMP_LEN)
-
-static char sms_bcd_chars[] = "0123456789*#abc\0\0";
-
-static void
-sms_semi_octets_to_bcd_string (char *dest, const guint8 *octets, int num_octets)
-{
- int i;
-
- for (i = 0 ; i < num_octets; i++) {
- *dest++ = sms_bcd_chars[octets[i] & 0xf];
- *dest++ = sms_bcd_chars[(octets[i] >> 4) & 0xf];
- }
- *dest++ = '\0';
-}
-
-static gboolean
-char_to_bcd (char in, guint8 *out)
-{
- guint32 z;
-
- if (isdigit (in)) {
- *out = in - 0x30;
- return TRUE;
- }
-
- for (z = 10; z < 16; z++) {
- if (in == sms_bcd_chars[z]) {
- *out = z;
- return TRUE;
- }
- }
- return FALSE;
-}
-
-static gsize
-sms_string_to_bcd_semi_octets (guint8 *buf, gsize buflen, const char *string)
-{
- guint i;
- guint8 bcd;
- gsize addrlen, slen;
-
- addrlen = slen = strlen (string);
- if (addrlen % 2)
- addrlen++;
- g_return_val_if_fail (buflen >= addrlen, 0);
-
- for (i = 0; i < addrlen; i += 2) {
- if (!char_to_bcd (string[i], &bcd))
- return 0;
- buf[i / 2] = bcd & 0xF;
-
- if (i >= slen - 1) {
- /* PDU address gets padded with 0xF if string is odd length */
- bcd = 0xF;
- } else if (!char_to_bcd (string[i + 1], &bcd))
- return 0;
- buf[i / 2] |= bcd << 4;
- }
- return addrlen / 2;
-}
-
-/* len is in semi-octets */
-static char *
-sms_decode_address (const guint8 *address, int len)
-{
- guint8 addrtype, addrplan;
- char *utf8;
-
- addrtype = address[0] & SMS_NUMBER_TYPE_MASK;
- addrplan = address[0] & SMS_NUMBER_PLAN_MASK;
- address++;
-
- if (addrtype == SMS_NUMBER_TYPE_ALPHA) {
- guint8 *unpacked;
- guint32 unpacked_len;
- unpacked = gsm_unpack (address, (len * 4) / 7, 0, &unpacked_len);
- utf8 = (char *)mm_charset_gsm_unpacked_to_utf8 (unpacked,
- unpacked_len);
- g_free(unpacked);
- } else if (addrtype == SMS_NUMBER_TYPE_INTL &&
- addrplan == SMS_NUMBER_PLAN_TELEPHONE) {
- /* International telphone number, format as "+1234567890" */
- utf8 = g_malloc (len + 3); /* '+' + digits + possible trailing 0xf + NUL */
- utf8[0] = '+';
- sms_semi_octets_to_bcd_string (utf8 + 1, address, (len + 1) / 2);
- } else {
- /*
- * All non-alphanumeric types and plans are just digits, but
- * don't apply any special formatting if we don't know the
- * format.
- */
- utf8 = g_malloc (len + 2); /* digits + possible trailing 0xf + NUL */
- sms_semi_octets_to_bcd_string (utf8, address, (len + 1) / 2);
- }
-
- return utf8;
-}
-
-static char *
-sms_decode_timestamp (const guint8 *timestamp)
-{
- /* YYMMDDHHMMSS+ZZ */
- char *timestr;
- int quarters, hours;
-
- timestr = g_malloc0 (16);
- sms_semi_octets_to_bcd_string (timestr, timestamp, 6);
- quarters = ((timestamp[6] & 0x7) * 10) + ((timestamp[6] >> 4) & 0xf);
- hours = quarters / 4;
- if (timestamp[6] & 0x08)
- timestr[12] = '-';
- else
- timestr[12] = '+';
- timestr[13] = (hours / 10) + '0';
- timestr[14] = (hours % 10) + '0';
- /* TODO(njw): Change timestamp rep to something that includes quarter-hours */
- return timestr;
-}
-
-static MMSmsEncoding
-sms_encoding_type (int dcs)
-{
- MMSmsEncoding scheme = MM_SMS_ENCODING_UNKNOWN;
-
- switch ((dcs >> 4) & 0xf) {
- /* General data coding group */
- case 0: case 1:
- case 2: case 3:
- switch (dcs & 0x0c) {
- case 0x08:
- scheme = MM_SMS_ENCODING_UCS2;
- break;
- case 0x00:
- /* fallthrough */
- /* reserved - spec says to treat it as default alphabet */
- case 0x0c:
- scheme = MM_SMS_ENCODING_GSM7;
- break;
- case 0x04:
- scheme = MM_SMS_ENCODING_8BIT;
- break;
- }
- break;
-
- /* Message waiting group (default alphabet) */
- case 0xc:
- case 0xd:
- scheme = MM_SMS_ENCODING_GSM7;
- break;
-
- /* Message waiting group (UCS2 alphabet) */
- case 0xe:
- scheme = MM_SMS_ENCODING_UCS2;
- break;
-
- /* Data coding/message class group */
- case 0xf:
- switch (dcs & 0x04) {
- case 0x00:
- scheme = MM_SMS_ENCODING_GSM7;
- break;
- case 0x04:
- scheme = MM_SMS_ENCODING_8BIT;
- break;
- }
- break;
-
- /* Reserved coding group values - spec says to treat it as default alphabet */
- default:
- scheme = MM_SMS_ENCODING_GSM7;
- break;
- }
-
- return scheme;
-}
-
-static char *
-sms_decode_text (const guint8 *text, int len, MMSmsEncoding encoding, int bit_offset)
-{
- char *utf8;
- guint8 *unpacked;
- guint32 unpacked_len;
-
- if (encoding == MM_SMS_ENCODING_GSM7) {
- mm_dbg ("Converting SMS part text from GSM7 to UTF8...");
- unpacked = gsm_unpack ((const guint8 *) text, len, bit_offset, &unpacked_len);
- utf8 = (char *) mm_charset_gsm_unpacked_to_utf8 (unpacked, unpacked_len);
- mm_dbg (" Got UTF-8 text: '%s'", utf8);
- g_free (unpacked);
- } else if (encoding == MM_SMS_ENCODING_UCS2) {
- mm_dbg ("Converting SMS part text from UCS-2BE to UTF8...");
- utf8 = g_convert ((char *) text, len, "UTF8", "UCS-2BE", NULL, NULL, NULL);
- mm_dbg (" Got UTF-8 text: '%s'", utf8);
- } else {
- g_warn_if_reached ();
- utf8 = g_strdup ("");
- }
-
- return utf8;
-}
-
-static guint
-relative_to_validity (guint8 relative)
-{
- if (relative <= 143)
- return (relative + 1) * 5;
-
- if (relative <= 167)
- return 720 + (relative - 143) * 30;
-
- return (relative - 166) * 1440;
-}
-
-static guint8
-validity_to_relative (guint validity)
-{
- if (validity == 0)
- return 167; /* 24 hours */
-
- if (validity <= 720) {
- /* 5 minute units up to 12 hours */
- if (validity % 5)
- validity += 5;
- return (validity / 5) - 1;
- }
-
- if (validity > 720 && validity <= 1440) {
- /* 12 hours + 30 minute units up to 1 day */
- if (validity % 30)
- validity += 30; /* round up to next 30 minutes */
- validity = MIN (validity, 1440);
- return 143 + ((validity - 720) / 30);
- }
-
- if (validity > 1440 && validity <= 43200) {
- /* 2 days up to 1 month */
- if (validity % 1440)
- validity += 1440; /* round up to next day */
- validity = MIN (validity, 43200);
- return 167 + ((validity - 1440) / 1440);
- }
-
- /* 43200 = 30 days in minutes
- * 10080 = 7 days in minutes
- * 635040 = 63 weeks in minutes
- * 40320 = 4 weeks in minutes
- */
- if (validity > 43200 && validity <= 635040) {
- /* 5 weeks up to 63 weeks */
- if (validity % 10080)
- validity += 10080; /* round up to next week */
- validity = MIN (validity, 635040);
- return 196 + ((validity - 40320) / 10080);
- }
-
- return 255; /* 63 weeks */
-}
-
struct _MMSmsPart {
guint index;
MMSmsPduType pdu_type;
@@ -336,6 +48,10 @@ struct _MMSmsPart {
guint concat_reference;
guint concat_max;
guint concat_sequence;
+
+ /* CDMA specific */
+ MMSmsCdmaTeleserviceId cdma_teleservice_id;
+ MMSmsCdmaServiceCategory cdma_service_category;
};
void
@@ -450,6 +166,11 @@ mm_sms_part_should_concat (MMSmsPart *self)
return self->should_concat;
}
+PART_GET_FUNC (MMSmsCdmaTeleserviceId, cdma_teleservice_id)
+PART_SET_FUNC (MMSmsCdmaTeleserviceId, cdma_teleservice_id)
+PART_GET_FUNC (MMSmsCdmaServiceCategory, cdma_service_category)
+PART_SET_FUNC (MMSmsCdmaServiceCategory, cdma_service_category)
+
MMSmsPart *
mm_sms_part_new (guint index,
MMSmsPduType pdu_type)
@@ -461,843 +182,9 @@ mm_sms_part_new (guint index,
sms_part->pdu_type = pdu_type;
sms_part->encoding = MM_SMS_ENCODING_UNKNOWN;
sms_part->delivery_state = MM_SMS_DELIVERY_STATE_UNKNOWN;
+ sms_part->cdma_teleservice_id = MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN;
+ sms_part->cdma_service_category = MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN;
sms_part->class = -1;
return sms_part;
}
-
-MMSmsPart *
-mm_sms_part_new_from_pdu (guint index,
- const gchar *hexpdu,
- GError **error)
-{
- gsize pdu_len;
- guint8 *pdu;
- MMSmsPart *part;
-
- /* Convert PDU from hex to binary */
- pdu = (guint8 *) mm_utils_hexstr2bin (hexpdu, &pdu_len);
- if (!pdu) {
- g_set_error_literal (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't convert PDU from hex to binary");
- return NULL;
- }
-
- part = mm_sms_part_new_from_binary_pdu (index, pdu, pdu_len, error);
- g_free (pdu);
-
- return part;
-}
-
-MMSmsPart *
-mm_sms_part_new_from_binary_pdu (guint index,
- const guint8 *pdu,
- gsize pdu_len,
- GError **error)
-{
- MMSmsPart *sms_part;
- guint8 pdu_type;
- guint offset;
- guint smsc_addr_size_bytes;
- guint tp_addr_size_digits;
- guint tp_addr_size_bytes;
- guint8 validity_format = 0;
- gboolean has_udh = FALSE;
- /* The following offsets are OPTIONAL, as STATUS REPORTs may not have
- * them; we use '0' to indicate their absence */
- guint tp_pid_offset = 0;
- guint tp_dcs_offset = 0;
- guint tp_user_data_len_offset = 0;
- MMSmsEncoding user_data_encoding = MM_SMS_ENCODING_UNKNOWN;
-
- /* Create the new MMSmsPart */
- sms_part = mm_sms_part_new (index, MM_SMS_PDU_TYPE_UNKNOWN);
-
- if (index != SMS_PART_INVALID_INDEX)
- mm_dbg ("Parsing PDU (%u)...", index);
- else
- mm_dbg ("Parsing PDU...");
-
-#define PDU_SIZE_CHECK(required_size, check_descr_str) \
- if (pdu_len < required_size) { \
- g_set_error (error, \
- MM_CORE_ERROR, \
- MM_CORE_ERROR_FAILED, \
- "PDU too short, %s: %" G_GSIZE_FORMAT " < %u", \
- check_descr_str, \
- pdu_len, \
- required_size); \
- mm_sms_part_free (sms_part); \
- return NULL; \
- }
-
- offset = 0;
-
- /* ---------------------------------------------------------------------- */
- /* SMSC, in address format, precedes the TPDU
- * First byte represents the number of BYTES for the address value */
- PDU_SIZE_CHECK (1, "cannot read SMSC address length");
- smsc_addr_size_bytes = pdu[offset++];
- if (smsc_addr_size_bytes > 0) {
- PDU_SIZE_CHECK (offset + smsc_addr_size_bytes, "cannot read SMSC address");
- /* SMSC may not be given in DELIVER PDUs */
- mm_sms_part_take_smsc (sms_part,
- sms_decode_address (&pdu[1], 2 * (smsc_addr_size_bytes - 1)));
- mm_dbg (" SMSC address parsed: '%s'", mm_sms_part_get_smsc (sms_part));
- offset += smsc_addr_size_bytes;
- } else
- mm_dbg (" No SMSC address given");
-
-
- /* ---------------------------------------------------------------------- */
- /* TP-MTI (1 byte) */
- PDU_SIZE_CHECK (offset + 1, "cannot read TP-MTI");
-
- pdu_type = (pdu[offset] & SMS_TP_MTI_MASK);
- switch (pdu_type) {
- case SMS_TP_MTI_SMS_DELIVER:
- mm_dbg (" Deliver type PDU detected");
- mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_DELIVER);
- break;
- case SMS_TP_MTI_SMS_SUBMIT:
- mm_dbg (" Submit type PDU detected");
- mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_SUBMIT);
- break;
- case SMS_TP_MTI_SMS_STATUS_REPORT:
- mm_dbg (" Status report type PDU detected");
- mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_STATUS_REPORT);
- break;
- default:
- mm_sms_part_free (sms_part);
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unhandled message type: 0x%02x",
- pdu_type);
- return NULL;
- }
-
- /* Delivery report was requested? */
- if (pdu[offset] & 0x20)
- mm_sms_part_set_delivery_report_request (sms_part, TRUE);
-
- /* PDU with validity? (only in SUBMIT PDUs) */
- if (pdu_type == SMS_TP_MTI_SMS_SUBMIT)
- validity_format = pdu[offset] & 0x18;
-
- /* PDU with user data header? */
- if (pdu[offset] & 0x40)
- has_udh = TRUE;
-
- offset++;
-
- /* ---------------------------------------------------------------------- */
- /* TP-MR (1 byte, in STATUS_REPORT and SUBMIT PDUs */
- if (pdu_type == SMS_TP_MTI_SMS_STATUS_REPORT ||
- pdu_type == SMS_TP_MTI_SMS_SUBMIT) {
- PDU_SIZE_CHECK (offset + 1, "cannot read message reference");
-
- mm_dbg (" message reference: %u", (guint)pdu[offset]);
- mm_sms_part_set_message_reference (sms_part, pdu[offset]);
- offset++;
- }
-
-
- /* ---------------------------------------------------------------------- */
- /* TP-DA or TP-OA or TP-RA
- * First byte represents the number of DIGITS in the number.
- * Round the sender address length up to an even number of
- * semi-octets, and thus an integral number of octets.
- */
- PDU_SIZE_CHECK (offset + 1, "cannot read number of digits in number");
- tp_addr_size_digits = pdu[offset++];
- tp_addr_size_bytes = (tp_addr_size_digits + 1) >> 1;
-
- PDU_SIZE_CHECK (offset + tp_addr_size_bytes, "cannot read number");
- mm_sms_part_take_number (sms_part,
- sms_decode_address (&pdu[offset],
- tp_addr_size_digits));
- mm_dbg (" Number parsed: '%s'", mm_sms_part_get_number (sms_part));
- offset += (1 + tp_addr_size_bytes); /* +1 due to the Type of Address byte */
-
- /* ---------------------------------------------------------------------- */
- /* Get timestamps and indexes for TP-PID, TP-DCS and TP-UDL/TP-UD */
-
- if (pdu_type == SMS_TP_MTI_SMS_DELIVER) {
- PDU_SIZE_CHECK (offset + 9,
- "cannot read PID/DCS/Timestamp"); /* 1+1+7=9 */
-
- /* ------ TP-PID (1 byte) ------ */
- tp_pid_offset = offset++;
-
- /* ------ TP-DCS (1 byte) ------ */
- tp_dcs_offset = offset++;
-
- /* ------ Timestamp (7 bytes) ------ */
- mm_sms_part_take_timestamp (sms_part,
- sms_decode_timestamp (&pdu[offset]));
- offset += 7;
-
- tp_user_data_len_offset = offset;
- } else if (pdu_type == SMS_TP_MTI_SMS_SUBMIT) {
- PDU_SIZE_CHECK (offset + 2 + !!validity_format,
- "cannot read PID/DCS/Validity"); /* 1+1=2 */
-
- /* ------ TP-PID (1 byte) ------ */
- tp_pid_offset = offset++;
-
- /* ------ TP-DCS (1 byte) ------ */
- tp_dcs_offset = offset++;
-
- /* ----------- TP-Validity-Period (1 byte) ----------- */
- if (validity_format) {
- switch (validity_format) {
- case 0x10:
- mm_dbg (" validity available, format relative");
- mm_sms_part_set_validity_relative (sms_part,
- relative_to_validity (pdu[offset]));
- offset++;
- break;
- case 0x08:
- /* TODO: support enhanced format; GSM 03.40 */
- mm_dbg (" validity available, format enhanced (not implemented)");
- /* 7 bytes for enhanced validity */
- offset += 7;
- break;
- case 0x18:
- /* TODO: support absolute format; GSM 03.40 */
- mm_dbg (" validity available, format absolute (not implemented)");
- /* 7 bytes for absolute validity */
- offset += 7;
- break;
- default:
- /* Cannot happen as we AND with the 0x18 mask */
- g_assert_not_reached();
- }
- }
-
- tp_user_data_len_offset = offset;
- }
- else if (pdu_type == SMS_TP_MTI_SMS_STATUS_REPORT) {
- /* We have 2 timestamps in status report PDUs:
- * first, the timestamp for when the PDU was received in the SMSC
- * second, the timestamp for when the PDU was forwarded by the SMSC
- */
- PDU_SIZE_CHECK (offset + 15, "cannot read Timestamps/TP-STATUS"); /* 7+7+1=15 */
-
- /* ------ Timestamp (7 bytes) ------ */
- mm_sms_part_take_timestamp (sms_part,
- sms_decode_timestamp (&pdu[offset]));
- offset += 7;
-
- /* ------ Discharge Timestamp (7 bytes) ------ */
- mm_sms_part_take_discharge_timestamp (sms_part,
- sms_decode_timestamp (&pdu[offset]));
- offset += 7;
-
- /* ----- TP-STATUS (1 byte) ------ */
- mm_dbg (" delivery state: %u", (guint)pdu[offset]);
- mm_sms_part_set_delivery_state (sms_part, pdu[offset]);
- offset++;
-
- /* ------ TP-PI (1 byte) OPTIONAL ------ */
- if (offset < pdu_len) {
- guint next_optional_field_offset = offset + 1;
-
- /* TP-PID? */
- if (pdu[offset] & 0x01)
- tp_pid_offset = next_optional_field_offset++;
-
- /* TP-DCS? */
- if (pdu[offset] & 0x02)
- tp_dcs_offset = next_optional_field_offset++;
-
- /* TP-UserData? */
- if (pdu[offset] & 0x04)
- tp_user_data_len_offset = next_optional_field_offset;
- }
- } else
- g_assert_not_reached ();
-
- if (tp_pid_offset > 0) {
- PDU_SIZE_CHECK (tp_pid_offset + 1, "cannot read TP-PID");
- mm_dbg (" PID: %u", (guint)pdu[tp_pid_offset]);
- }
-
- /* Grab user data encoding and message class */
- if (tp_dcs_offset > 0) {
- PDU_SIZE_CHECK (tp_dcs_offset + 1, "cannot read TP-DCS");
-
- /* Encoding given in the 'alphabet' bits */
- user_data_encoding = sms_encoding_type(pdu[tp_dcs_offset]);
- switch (user_data_encoding) {
- case MM_SMS_ENCODING_GSM7:
- mm_dbg (" user data encoding is GSM7");
- break;
- case MM_SMS_ENCODING_UCS2:
- mm_dbg (" user data encoding is UCS2");
- break;
- case MM_SMS_ENCODING_8BIT:
- mm_dbg (" user data encoding is 8bit");
- break;
- default:
- mm_dbg (" user data encoding is unknown");
- break;
- }
- mm_sms_part_set_encoding (sms_part, user_data_encoding);
-
- /* Class */
- if (pdu[tp_dcs_offset] & SMS_DCS_CLASS_VALID)
- mm_sms_part_set_class (sms_part,
- pdu[tp_dcs_offset] & SMS_DCS_CLASS_MASK);
- }
-
- if (tp_user_data_len_offset > 0) {
- guint tp_user_data_size_elements;
- guint tp_user_data_size_bytes;
- guint tp_user_data_offset;
- guint bit_offset;
-
- PDU_SIZE_CHECK (tp_user_data_len_offset + 1, "cannot read TP-UDL");
- tp_user_data_size_elements = pdu[tp_user_data_len_offset];
- mm_dbg (" user data length: %u elements", tp_user_data_size_elements);
-
- if (user_data_encoding == MM_SMS_ENCODING_GSM7)
- tp_user_data_size_bytes = (7 * (tp_user_data_size_elements + 1 )) / 8;
- else
- tp_user_data_size_bytes = tp_user_data_size_elements;
- mm_dbg (" user data length: %u bytes", tp_user_data_size_bytes);
-
- tp_user_data_offset = tp_user_data_len_offset + 1;
- PDU_SIZE_CHECK (tp_user_data_offset + tp_user_data_size_bytes, "cannot read TP-UD");
-
- bit_offset = 0;
- if (has_udh) {
- guint udhl, end;
-
- udhl = pdu[tp_user_data_offset] + 1;
- end = tp_user_data_offset + udhl;
-
- PDU_SIZE_CHECK (tp_user_data_offset + udhl, "cannot read UDH");
-
- for (offset = tp_user_data_offset + 1; (offset + 1) < end;) {
- guint8 ie_id, ie_len;
-
- ie_id = pdu[offset++];
- ie_len = pdu[offset++];
-
- switch (ie_id) {
- case 0x00:
- if (offset + 2 >= end)
- break;
- /*
- * Ignore the IE if one of the following is true:
- * - it claims to be part 0 of M
- * - it claims to be part N of M, N > M
- */
- if (pdu[offset + 2] == 0 ||
- pdu[offset + 2] > pdu[offset + 1])
- break;
-
- mm_sms_part_set_concat_reference (sms_part, pdu[offset]);
- mm_sms_part_set_concat_max (sms_part, pdu[offset + 1]);
- mm_sms_part_set_concat_sequence (sms_part, pdu[offset + 2]);
- break;
- case 0x08:
- if (offset + 3 >= end)
- break;
- /* Concatenated short message, 16-bit reference */
- if (pdu[offset + 3] == 0 ||
- pdu[offset + 3] > pdu[offset + 2])
- break;
-
- mm_sms_part_set_concat_reference (sms_part, (pdu[offset] << 8) | pdu[offset + 1]);
- mm_sms_part_set_concat_max (sms_part,pdu[offset + 2]);
- mm_sms_part_set_concat_sequence (sms_part, pdu[offset + 3]);
- break;
- }
-
- offset += ie_len;
- }
-
- /*
- * Move past the user data headers to prevent it from being
- * decoded into garbage text.
- */
- tp_user_data_offset += udhl;
- tp_user_data_size_bytes -= udhl;
- if (user_data_encoding == MM_SMS_ENCODING_GSM7) {
- /*
- * Find the number of bits we need to add to the length of the
- * user data to get a multiple of 7 (the padding).
- */
- bit_offset = (7 - udhl % 7) % 7;
- tp_user_data_size_elements -= (udhl * 8 + bit_offset) / 7;
- } else
- tp_user_data_size_elements -= udhl;
- }
-
- switch (user_data_encoding) {
- case MM_SMS_ENCODING_GSM7:
- case MM_SMS_ENCODING_UCS2:
- /* Otherwise if it's 7-bit or UCS2 we can decode it */
- mm_dbg ("Decoding SMS text with '%u' elements", tp_user_data_size_elements);
- mm_sms_part_take_text (sms_part,
- sms_decode_text (&pdu[tp_user_data_offset],
- tp_user_data_size_elements,
- user_data_encoding,
- bit_offset));
- g_warn_if_fail (sms_part->text != NULL);
- break;
-
- default:
- {
- GByteArray *raw;
-
- mm_dbg ("Skipping SMS text: Unknown encoding (0x%02X)", user_data_encoding);
-
- PDU_SIZE_CHECK (tp_user_data_offset + tp_user_data_size_bytes, "cannot read user data");
-
- /* 8-bit encoding is usually binary data, and we have no idea what
- * actual encoding the data is in so we can't convert it.
- */
- raw = g_byte_array_sized_new (tp_user_data_size_bytes);
- g_byte_array_append (raw, &pdu[tp_user_data_offset], tp_user_data_size_bytes);
- mm_sms_part_take_data (sms_part, raw);
- break;
- }
- }
- }
-
- return sms_part;
-}
-
-/**
- * mm_sms_part_encode_address:
- *
- * @address: the phone number to encode
- * @buf: the buffer to encode @address in
- * @buflen: the size of @buf
- * @is_smsc: if %TRUE encode size as number of octets of address infromation,
- * otherwise if %FALSE encode size as number of digits of @address
- *
- * Returns: the size in bytes of the data added to @buf
- **/
-guint
-mm_sms_part_encode_address (const gchar *address,
- guint8 *buf,
- gsize buflen,
- gboolean is_smsc)
-{
- gsize len;
-
- g_return_val_if_fail (address != NULL, 0);
- g_return_val_if_fail (buf != NULL, 0);
- g_return_val_if_fail (buflen >= 2, 0);
-
- /* Handle number type & plan */
- buf[1] = 0x80; /* Bit 7 always 1 */
- if (address[0] == '+') {
- buf[1] |= SMS_NUMBER_TYPE_INTL;
- address++;
- }
- buf[1] |= SMS_NUMBER_PLAN_TELEPHONE;
-
- len = sms_string_to_bcd_semi_octets (&buf[2], buflen, address);
-
- if (is_smsc)
- buf[0] = len + 1; /* addr length + size byte */
- else
- buf[0] = strlen (address); /* number of digits in address */
-
- return len ? len + 2 : 0; /* addr length + size byte + number type/plan */
-}
-
-/**
- * mm_sms_part_get_submit_pdu:
- *
- * @part: the SMS message part
- * @out_pdulen: on success, the size of the returned PDU in bytes
- * @out_msgstart: on success, the byte index in the returned PDU where the
- * message starts (ie, skipping the SMSC length byte and address, if present)
- * @error: on error, filled with the error that occurred
- *
- * Constructs a single-part SMS message with the given details, preferring to
- * use the UCS2 character set when the message will fit, otherwise falling back
- * to the GSM character set.
- *
- * Returns: the constructed PDU data on success, or %NULL on error
- **/
-guint8 *
-mm_sms_part_get_submit_pdu (MMSmsPart *part,
- guint *out_pdulen,
- guint *out_msgstart,
- GError **error)
-{
- guint8 *pdu;
- guint len, offset = 0;
- guint shift = 0;
- guint8 *udl_ptr;
-
- g_return_val_if_fail (part->number != NULL, NULL);
- g_return_val_if_fail (part->text != NULL || part->data != NULL, NULL);
-
- mm_dbg ("Creating PDU for part...");
-
- /* Build up the PDU */
- pdu = g_malloc0 (PDU_SIZE);
-
- if (part->smsc) {
- mm_dbg (" adding SMSC to PDU...");
- len = mm_sms_part_encode_address (part->smsc, pdu, PDU_SIZE, TRUE);
- if (len == 0) {
- g_set_error (error,
- MM_MESSAGE_ERROR,
- MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
- "Invalid SMSC address '%s'", part->smsc);
- goto error;
- }
- offset += len;
- } else {
- /* No SMSC, use default */
- pdu[offset++] = 0x00;
- }
-
- if (out_msgstart)
- *out_msgstart = offset;
-
- /* ----------- First BYTE ----------- */
- pdu[offset] = 0;
-
- /* TP-VP present; format RELATIVE */
- if (part->validity_relative > 0) {
- mm_dbg (" adding validity to PDU...");
- pdu[offset] |= 0x10;
- }
-
- /* Concatenation sequence only found in multipart SMS */
- if (part->concat_sequence) {
- mm_dbg (" adding UDHI to PDU...");
- pdu[offset] |= 0x40; /* UDHI */
- }
-
- /* Delivery report requested in singlepart messages or in the last PDU of
- * multipart messages */
- if (part->delivery_report_request &&
- (!part->concat_sequence ||
- part->concat_max == part->concat_sequence)) {
- mm_dbg (" requesting delivery report...");
- pdu[offset] |= 0x20;
- }
-
- /* TP-MTI = SMS-SUBMIT */
- pdu[offset++] |= 0x01;
-
-
- /* ----------- TP-MR (1 byte) ----------- */
-
- pdu[offset++] = 0x00; /* TP-Message-Reference: filled by device */
-
- /* ----------- Destination address ----------- */
-
- len = mm_sms_part_encode_address (part->number, &pdu[offset], PDU_SIZE - offset, FALSE);
- if (len == 0) {
- g_set_error (error,
- MM_MESSAGE_ERROR,
- MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
- "Invalid number '%s'", part->number);
- goto error;
- }
- offset += len;
-
- /* ----------- TP-PID (1 byte) ----------- */
-
- pdu[offset++] = 0x00;
-
- /* ----------- TP-DCS (1 byte) ----------- */
- pdu[offset] = 0x00;
-
- if (part->class >= 0 && part->class <= 3) {
- mm_dbg (" using class %d...", part->class);
- pdu[offset] |= SMS_DCS_CLASS_VALID;
- pdu[offset] |= part->class;
- }
-
- if (part->encoding == MM_SMS_ENCODING_UCS2) {
- mm_dbg (" using UCS2 encoding...");
- pdu[offset] |= SMS_DCS_CODING_UCS2;
- } else if (part->encoding == MM_SMS_ENCODING_GSM7) {
- mm_dbg (" using GSM7 encoding...");
- pdu[offset] |= SMS_DCS_CODING_DEFAULT; /* GSM */
- } else {
- mm_dbg (" using 8bit encoding...");
- pdu[offset] |= SMS_DCS_CODING_8BIT;
- }
- offset++;
-
- /* ----------- TP-Validity-Period (1 byte): 4 days ----------- */
- /* Only if TP-VPF was set in first byte */
-
- if (part->validity_relative > 0)
- pdu[offset++] = validity_to_relative (part->validity_relative);
-
- /* ----------- TP-User-Data-Length ----------- */
- /* Set to zero initially, and keep a ptr for easy access later */
- udl_ptr = &pdu[offset];
- pdu[offset++] = 0;
-
- /* Build UDH */
- if (part->concat_sequence) {
- mm_dbg (" adding UDH header in PDU... (reference: %u, max: %u, sequence: %u)",
- part->concat_reference,
- part->concat_max,
- part->concat_sequence);
- pdu[offset++] = 0x05; /* udh len */
- pdu[offset++] = 0x00; /* mid */
- pdu[offset++] = 0x03; /* data len */
- pdu[offset++] = (guint8)part->concat_reference;
- pdu[offset++] = (guint8)part->concat_max;
- pdu[offset++] = (guint8)part->concat_sequence;
-
- /* if a UDH is present and the data encoding is the default 7-bit
- * alphabet, the user data must be 7-bit word aligned after the
- * UDH. This means up to 6 bits of zeros need to be inserted at the
- * start of the message.
- *
- * In our case the UDH is 6 bytes long, 48bits. The next multiple of
- * 7 is therefore 49, so we only need to include one bit of padding.
- */
- shift = 1;
- }
-
- if (part->encoding == MM_SMS_ENCODING_GSM7) {
- guint8 *unpacked, *packed;
- guint32 unlen = 0, packlen = 0;
-
- unpacked = mm_charset_utf8_to_unpacked_gsm (part->text, &unlen);
- if (!unpacked || unlen == 0) {
- g_free (unpacked);
- g_set_error_literal (error,
- MM_MESSAGE_ERROR,
- MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
- "Failed to convert message text to GSM");
- goto error;
- }
-
- /* Set real data length, in septets
- * If we had UDH, add 7 septets
- */
- *udl_ptr = part->concat_sequence ? (7 + unlen) : unlen;
- mm_dbg (" user data length is '%u' septets (%s UDH)",
- *udl_ptr,
- part->concat_sequence ? "with" : "without");
-
- packed = gsm_pack (unpacked, unlen, shift, &packlen);
- g_free (unpacked);
- if (!packed || packlen == 0) {
- g_free (packed);
- g_set_error_literal (error,
- MM_MESSAGE_ERROR,
- MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
- "Failed to pack message text to GSM");
- goto error;
- }
-
- memcpy (&pdu[offset], packed, packlen);
- g_free (packed);
- offset += packlen;
- } else if (part->encoding == MM_SMS_ENCODING_UCS2) {
- GByteArray *array;
-
- /* Try to guess a good value for the array */
- array = g_byte_array_sized_new (strlen (part->text) * 2);
- if (!mm_modem_charset_byte_array_append (array, part->text, FALSE, MM_MODEM_CHARSET_UCS2)) {
- g_byte_array_free (array, TRUE);
- g_set_error_literal (error,
- MM_MESSAGE_ERROR,
- MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
- "Failed to convert message text to UCS2");
- goto error;
- }
-
- /* Set real data length, in octets
- * If we had UDH, add 6 octets
- */
- *udl_ptr = part->concat_sequence ? (6 + array->len) : array->len;
- mm_dbg (" user data length is '%u' octets (%s UDH)",
- *udl_ptr,
- part->concat_sequence ? "with" : "without");
-
- memcpy (&pdu[offset], array->data, array->len);
- offset += array->len;
- g_byte_array_free (array, TRUE);
- } else if (part->encoding == MM_SMS_ENCODING_8BIT) {
- /* Set real data length, in octets
- * If we had UDH, add 6 octets
- */
- *udl_ptr = part->concat_sequence ? (6 + part->data->len) : part->data->len;
- mm_dbg (" binary user data length is '%u' octets (%s UDH)",
- *udl_ptr,
- part->concat_sequence ? "with" : "without");
-
- memcpy (&pdu[offset], part->data->data, part->data->len);
- offset += part->data->len;
- } else
- g_assert_not_reached ();
-
- if (out_pdulen)
- *out_pdulen = offset;
- return pdu;
-
-error:
- g_free (pdu);
- return NULL;
-}
-
-gchar **
-mm_sms_part_util_split_text (const gchar *text,
- MMSmsEncoding *encoding)
-{
- guint gsm_unsupported = 0;
- gchar **out;
- guint n_chunks;
- guint i;
- guint j;
- gsize in_len;
-
- if (!text)
- return NULL;
-
- in_len = strlen (text);
-
- /* Some info about the rules for splitting.
- *
- * The User Data can be up to 140 bytes in the SMS part:
- * 0) If we only need one chunk, it can be of up to 140 bytes.
- * If we need more than one chunk, these have to be of 140 - 6 = 134
- * bytes each, as we need place for the UDH header.
- * 1) If we're using GSM7 encoding, this gives us up to 160 characters,
- * as we can pack 160 characters of 7bits each into 140 bytes.
- * 160 * 7 = 140 * 8 = 1120.
- * If we only have 134 bytes allowed, that would mean that we can pack
- * up to 153 input characters:
- * 134 * 8 = 1072; 1072/7=153.14
- * 2) If we're using UCS2 encoding, we can pack up to 70 characters in
- * 140 bytes (each with 2 bytes), or up to 67 characters in 134 bytes.
- *
- * This method does the split of the input string into N strings, so that
- * each of the strings can be placed in a SMS part.
- */
-
- /* Check if we can do GSM encoding */
- mm_charset_get_encoded_len (text,
- MM_MODEM_CHARSET_GSM,
- &gsm_unsupported);
- if (gsm_unsupported > 0) {
- /* If cannot do it in GSM encoding, do it in UCS-2 */
- GByteArray *array;
-
- *encoding = MM_SMS_ENCODING_UCS2;
-
- /* Guess more or less the size of the output array to avoid multiple
- * allocations */
- array = g_byte_array_sized_new (in_len * 2);
- if (!mm_modem_charset_byte_array_append (array,
- text,
- FALSE,
- MM_MODEM_CHARSET_UCS2)) {
- g_byte_array_unref (array);
- return NULL;
- }
-
- /* Our bytearray has it in UCS-2 now.
- * UCS-2 is a fixed-size encoding, which means that the text has exactly
- * 2 bytes for each unicode point. We can now split this array into
- * chunks of 67 UCS-2 characters (134 bytes).
- *
- * Note that UCS-2 covers unicode points between U+0000 and U+FFFF, which
- * means that there is no direct relationship between the size of the
- * input text in UTF-8 and the size of the text in UCS-2. A 3-byte UTF-8
- * encoded character will still be represented with 2 bytes in UCS-2.
- */
- if (array->len <= 140) {
- out = g_new (gchar *, 2);
- out[0] = g_strdup (text);
- out[1] = NULL;
- } else {
- n_chunks = array->len / 134;
- if (array->len % 134 != 0)
- n_chunks++;
-
- out = g_new0 (gchar *, n_chunks + 1);
- for (i = 0, j = 0; i < n_chunks; i++, j += 134) {
- out[i] = sms_decode_text (&array->data[j],
- MIN (array->len - j, 134),
- MM_SMS_ENCODING_UCS2,
- 0);
- }
- }
- g_byte_array_unref (array);
- } else {
- /* Do it with GSM encoding */
- *encoding = MM_SMS_ENCODING_GSM7;
-
- if (in_len <= 160) {
- out = g_new (gchar *, 2);
- out[0] = g_strdup (text);
- out[1] = NULL;
- } else {
- n_chunks = in_len / 153;
- if (in_len % 153 != 0)
- n_chunks++;
-
- out = g_new0 (gchar *, n_chunks + 1);
- for (i = 0, j = 0; i < n_chunks; i++, j += 153) {
- out[i] = g_strndup (&text[j], 153);
- }
- }
- }
-
- return out;
-}
-
-GByteArray **
-mm_sms_part_util_split_data (const guint8 *data,
- gsize data_len)
-{
- GByteArray **out;
-
- /* Some info about the rules for splitting.
- *
- * The User Data can be up to 140 bytes in the SMS part:
- * 0) If we only need one chunk, it can be of up to 140 bytes.
- * If we need more than one chunk, these have to be of 140 - 6 = 134
- * bytes each, as we need place for the UDH header.
- */
-
- if (data_len <= 140) {
- out = g_new0 (GByteArray *, 2);
- out[0] = g_byte_array_append (g_byte_array_sized_new (data_len),
- data,
- data_len);
- } else {
- guint n_chunks;
- guint i;
- guint j;
-
- n_chunks = data_len / 134;
- if (data_len % 134 != 0)
- n_chunks ++;
-
- out = g_new0 (GByteArray *, n_chunks + 1);
- for (i = 0, j = 0; i < n_chunks; i++, j+= 134) {
- out[i] = g_byte_array_append (g_byte_array_sized_new (134),
- &data[j],
- MIN (data_len - j, 134));
- }
- }
-
- return out;
-}
diff --git a/src/mm-sms-part.h b/src/mm-sms-part.h
index d44ed5b..9626295 100644
--- a/src/mm-sms-part.h
+++ b/src/mm-sms-part.h
@@ -29,25 +29,20 @@ typedef enum {
typedef struct _MMSmsPart MMSmsPart;
-#define SMS_MAX_PDU_LEN 344
#define SMS_PART_INVALID_INDEX G_MAXUINT
+#define MM_SMS_PART_IS_3GPP(part) \
+ (mm_sms_part_get_pdu_type (part) >= MM_SMS_PDU_TYPE_DELIVER && \
+ mm_sms_part_get_pdu_type (part) <= MM_SMS_PDU_TYPE_STATUS_REPORT)
+
+#define MM_SMS_PART_IS_CDMA(part) \
+ (mm_sms_part_get_pdu_type (part) >= MM_SMS_PDU_TYPE_CDMA_DELIVER && \
+ mm_sms_part_get_pdu_type (part) <= MM_SMS_PDU_TYPE_CDMA_READ_ACKNOWLEDGEMENT)
+
MMSmsPart *mm_sms_part_new (guint index,
MMSmsPduType type);
-MMSmsPart *mm_sms_part_new_from_pdu (guint index,
- const gchar *hexpdu,
- GError **error);
-MMSmsPart *mm_sms_part_new_from_binary_pdu (guint index,
- const guint8 *pdu,
- gsize pdu_len,
- GError **error);
void mm_sms_part_free (MMSmsPart *part);
-guint8 *mm_sms_part_get_submit_pdu (MMSmsPart *part,
- guint *out_pdulen,
- guint *out_msgstart,
- GError **error);
-
guint mm_sms_part_get_index (MMSmsPart *part);
void mm_sms_part_set_index (MMSmsPart *part,
guint index);
@@ -129,16 +124,12 @@ void mm_sms_part_set_concat_sequence (MMSmsPart *part,
gboolean mm_sms_part_should_concat (MMSmsPart *part);
-/* For testcases only */
-guint mm_sms_part_encode_address (const gchar *address,
- guint8 *buf,
- gsize buflen,
- gboolean is_smsc);
-
-gchar **mm_sms_part_util_split_text (const gchar *text,
- MMSmsEncoding *encoding);
-
-GByteArray **mm_sms_part_util_split_data (const guint8 *data,
- gsize data_len);
+/* CDMA specific */
+MMSmsCdmaTeleserviceId mm_sms_part_get_cdma_teleservice_id (MMSmsPart *part);
+void mm_sms_part_set_cdma_teleservice_id (MMSmsPart *part,
+ MMSmsCdmaTeleserviceId cdma_teleservice_id);
+MMSmsCdmaServiceCategory mm_sms_part_get_cdma_service_category (MMSmsPart *part);
+void mm_sms_part_set_cdma_service_category (MMSmsPart *part,
+ MMSmsCdmaServiceCategory cdma_service_category);
#endif /* MM_SMS_PART_H */
diff --git a/src/mm-sms-qmi.c b/src/mm-sms-qmi.c
index 85ca516..c7d11c7 100644
--- a/src/mm-sms-qmi.c
+++ b/src/mm-sms-qmi.c
@@ -25,9 +25,12 @@
#include <libmm-glib.h>
#include "mm-modem-helpers-qmi.h"
+#include "mm-iface-modem.h"
#include "mm-iface-modem-messaging.h"
#include "mm-sms-qmi.h"
#include "mm-base-modem.h"
+#include "mm-sms-part-3gpp.h"
+#include "mm-sms-part-cdma.h"
#include "mm-log.h"
G_DEFINE_TYPE (MMSmsQmi, mm_sms_qmi, MM_TYPE_SMS);
@@ -82,6 +85,33 @@ ensure_qmi_client (MMSmsQmi *self,
}
/*****************************************************************************/
+
+static gboolean
+check_sms_type_support (MMSmsQmi *self,
+ MMBaseModem *modem,
+ MMSmsPart *first_part,
+ GError **error)
+{
+ if (MM_SMS_PART_IS_3GPP (first_part) && !mm_iface_modem_is_3gpp (MM_IFACE_MODEM (modem))) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Non-3GPP modem doesn't support 3GPP SMS");
+ return FALSE;
+ }
+
+ if (MM_SMS_PART_IS_CDMA (first_part) && !mm_iface_modem_is_cdma (MM_IFACE_MODEM (modem))) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Non-CDMA modem doesn't support CDMA SMS");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************/
/* Store the SMS */
typedef struct {
@@ -96,7 +126,7 @@ typedef struct {
static void
sms_store_context_complete_and_free (SmsStoreContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
+ g_simple_async_result_complete_in_idle (ctx->result);
g_object_unref (ctx->result);
g_object_unref (ctx->client);
g_object_unref (ctx->modem);
@@ -159,7 +189,7 @@ static void
sms_store_next_part (SmsStoreContext *ctx)
{
QmiMessageWmsRawWriteInput *input;
- guint8 *pdu;
+ guint8 *pdu = NULL;
guint pdulen = 0;
guint msgstart = 0;
GArray *array;
@@ -173,10 +203,22 @@ sms_store_next_part (SmsStoreContext *ctx)
}
/* Get PDU */
- pdu = mm_sms_part_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, &msgstart, &error);
+ if (MM_SMS_PART_IS_3GPP ((MMSmsPart *)ctx->current->data))
+ pdu = mm_sms_part_3gpp_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, &msgstart, &error);
+ else if (MM_SMS_PART_IS_CDMA ((MMSmsPart *)ctx->current->data))
+ pdu = mm_sms_part_cdma_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, &error);
+
if (!pdu) {
- /* 'error' should already be set */
- g_simple_async_result_take_error (ctx->result, error);
+ if (error)
+ g_simple_async_result_take_error (ctx->result, error);
+ else
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unknown or unsupported PDU type in SMS part: %s",
+ mm_sms_pdu_type_get_string (
+ mm_sms_part_get_pdu_type (
+ (MMSmsPart *)ctx->current->data)));
sms_store_context_complete_and_free (ctx);
return;
}
@@ -192,7 +234,9 @@ sms_store_next_part (SmsStoreContext *ctx)
qmi_message_wms_raw_write_input_set_raw_message_data (
input,
mm_sms_storage_to_qmi_storage_type (ctx->storage),
- QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_POINT_TO_POINT,
+ (MM_SMS_PART_IS_3GPP ((MMSmsPart *)ctx->current->data) ?
+ QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_POINT_TO_POINT :
+ QMI_WMS_MESSAGE_FORMAT_CDMA),
array,
NULL);
qmi_client_wms_raw_write (ctx->client,
@@ -213,6 +257,7 @@ sms_store (MMSms *self,
{
SmsStoreContext *ctx;
QmiClient *client = NULL;
+ GError *error = NULL;
/* Ensure WMS client */
if (!ensure_qmi_client (MM_SMS_QMI (self),
@@ -234,6 +279,15 @@ sms_store (MMSms *self,
NULL);
ctx->current = mm_sms_get_parts (self);
+
+ /* Check whether we support the given SMS type */
+ if (!check_sms_type_support (MM_SMS_QMI (self), ctx->modem, (MMSmsPart *)ctx->current->data, &error)) {
+ g_simple_async_result_take_error (ctx->result, error);
+ sms_store_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Go on */
sms_store_next_part (ctx);
}
@@ -325,16 +379,29 @@ static void
sms_send_generic (SmsSendContext *ctx)
{
QmiMessageWmsRawSendInput *input;
- guint8 *pdu;
+ guint8 *pdu = NULL;
guint pdulen = 0;
guint msgstart = 0;
GArray *array;
GError *error = NULL;
/* Get PDU */
- pdu = mm_sms_part_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, &msgstart, &error);
+ if (MM_SMS_PART_IS_3GPP ((MMSmsPart *)ctx->current->data))
+ pdu = mm_sms_part_3gpp_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, &msgstart, &error);
+ else if (MM_SMS_PART_IS_CDMA ((MMSmsPart *)ctx->current->data))
+ pdu = mm_sms_part_cdma_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, &error);
+
if (!pdu) {
- g_simple_async_result_take_error (ctx->result, error);
+ if (error)
+ g_simple_async_result_take_error (ctx->result, error);
+ else
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unknown or unsupported PDU type in SMS part: %s",
+ mm_sms_pdu_type_get_string (
+ mm_sms_part_get_pdu_type (
+ (MMSmsPart *)ctx->current->data)));
sms_send_context_complete_and_free (ctx);
return;
}
@@ -346,10 +413,11 @@ sms_send_generic (SmsSendContext *ctx)
g_free (pdu);
input = qmi_message_wms_raw_send_input_new ();
-
qmi_message_wms_raw_send_input_set_raw_message_data (
input,
- QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_POINT_TO_POINT,
+ (MM_SMS_PART_IS_3GPP ((MMSmsPart *)ctx->current->data) ?
+ QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_POINT_TO_POINT :
+ QMI_WMS_MESSAGE_FORMAT_CDMA),
array,
NULL);
@@ -404,6 +472,8 @@ send_from_storage_ready (QmiClientWms *client,
} else {
QmiWmsGsmUmtsRpCause rp_cause;
QmiWmsGsmUmtsTpCause tp_cause;
+ QmiWmsCdmaCauseCode cdma_cause_code;
+ QmiWmsCdmaErrorClass cdma_error_class;
if (qmi_message_wms_send_from_memory_storage_output_get_gsm_wcdma_cause_info (
output,
@@ -417,6 +487,24 @@ send_from_storage_ready (QmiClientWms *client,
qmi_wms_gsm_umts_tp_cause_get_string (tp_cause));
}
+ if (qmi_message_wms_send_from_memory_storage_output_get_cdma_cause_code (
+ output,
+ &cdma_cause_code,
+ NULL)) {
+ mm_warn ("Couldn't send SMS; cause code (%u): '%s'",
+ cdma_cause_code,
+ qmi_wms_cdma_cause_code_get_string (cdma_cause_code));
+ }
+
+ if (qmi_message_wms_send_from_memory_storage_output_get_cdma_error_class (
+ output,
+ &cdma_error_class,
+ NULL)) {
+ mm_warn ("Couldn't send SMS; error class (%u): '%s'",
+ cdma_error_class,
+ qmi_wms_cdma_error_class_get_string (cdma_error_class));
+ }
+
g_prefix_error (&error, "Couldn't write SMS part: ");
g_simple_async_result_take_error (ctx->result, error);
sms_send_context_complete_and_free (ctx);
@@ -448,7 +536,9 @@ sms_send_from_storage (SmsSendContext *ctx)
input,
mm_sms_storage_to_qmi_storage_type (mm_sms_get_storage (ctx->self)),
mm_sms_part_get_index ((MMSmsPart *)ctx->current->data),
- QMI_WMS_MESSAGE_MODE_GSM_WCDMA,
+ (MM_SMS_PART_IS_3GPP ((MMSmsPart *)ctx->current->data) ?
+ QMI_WMS_MESSAGE_MODE_GSM_WCDMA :
+ QMI_WMS_MESSAGE_MODE_CDMA),
NULL);
qmi_client_wms_send_from_memory_storage (
@@ -485,6 +575,7 @@ sms_send (MMSms *self,
{
SmsSendContext *ctx;
QmiClient *client = NULL;
+ GError *error = NULL;
/* Ensure WMS client */
if (!ensure_qmi_client (MM_SMS_QMI (self),
@@ -507,7 +598,15 @@ sms_send (MMSms *self,
/* If the SMS is STORED, try to send from storage */
ctx->from_storage = (mm_sms_get_storage (self) != MM_SMS_STORAGE_UNKNOWN);
- ctx->current = mm_sms_get_parts (self);;
+ ctx->current = mm_sms_get_parts (self);
+
+ /* Check whether we support the given SMS type */
+ if (!check_sms_type_support (MM_SMS_QMI (self), ctx->modem, (MMSmsPart *)ctx->current->data, &error)) {
+ g_simple_async_result_take_error (ctx->result, error);
+ sms_send_context_complete_and_free (ctx);
+ return;
+ }
+
sms_send_next_part (ctx);
}
@@ -612,7 +711,9 @@ delete_next_part (SmsDeletePartsContext *ctx)
NULL);
qmi_message_wms_delete_input_set_message_mode (
input,
- QMI_WMS_MESSAGE_MODE_GSM_WCDMA,
+ (MM_SMS_PART_IS_3GPP ((MMSmsPart *)ctx->current->data) ?
+ QMI_WMS_MESSAGE_MODE_GSM_WCDMA:
+ QMI_WMS_MESSAGE_MODE_CDMA),
NULL);
qmi_client_wms_delete (ctx->client,
input,
diff --git a/src/mm-sms.c b/src/mm-sms.c
index a7b1d64..672d5bc 100644
--- a/src/mm-sms.c
+++ b/src/mm-sms.c
@@ -30,6 +30,7 @@
#include "mm-iface-modem.h"
#include "mm-iface-modem-messaging.h"
#include "mm-sms.h"
+#include "mm-sms-part-3gpp.h"
#include "mm-base-modem-at.h"
#include "mm-base-modem.h"
#include "mm-log.h"
@@ -94,8 +95,8 @@ get_validity_relative (GVariant *tuple)
}
static gboolean
-generate_submit_pdus (MMSms *self,
- GError **error)
+generate_3gpp_submit_pdus (MMSms *self,
+ GError **error)
{
guint i;
guint n_parts;
@@ -123,7 +124,7 @@ generate_submit_pdus (MMSms *self,
g_assert (!(text != NULL && data != NULL));
if (text) {
- split_text = mm_sms_part_util_split_text (text, &encoding);
+ split_text = mm_sms_part_3gpp_util_split_text (text, &encoding);
if (!split_text) {
g_set_error (error,
MM_CORE_ERROR,
@@ -134,7 +135,7 @@ generate_submit_pdus (MMSms *self,
n_parts = g_strv_length (split_text);
} else if (data) {
encoding = MM_SMS_ENCODING_8BIT;
- split_data = mm_sms_part_util_split_data (data, data_len);
+ split_data = mm_sms_part_3gpp_util_split_data (data, data_len);
g_assert (split_data != NULL);
/* noop within the for */
for (n_parts = 0; split_data[n_parts]; n_parts++);
@@ -231,6 +232,90 @@ generate_submit_pdus (MMSms *self,
return TRUE;
}
+static gboolean
+generate_cdma_submit_pdus (MMSms *self,
+ GError **error)
+{
+ const gchar *text;
+ GVariant *data_variant;
+ const guint8 *data;
+ gsize data_len = 0;
+
+ MMSmsPart *part;
+
+ g_assert (self->priv->parts == NULL);
+
+ text = mm_gdbus_sms_get_text (MM_GDBUS_SMS (self));
+ data_variant = mm_gdbus_sms_get_data (MM_GDBUS_SMS (self));
+ data = (data_variant ?
+ g_variant_get_fixed_array (data_variant,
+ &data_len,
+ sizeof (guchar)) :
+ NULL);
+
+ g_assert (text != NULL || data != NULL);
+ g_assert (!(text != NULL && data != NULL));
+
+ /* Create new part */
+ part = mm_sms_part_new (SMS_PART_INVALID_INDEX, MM_SMS_PDU_TYPE_CDMA_SUBMIT);
+ if (text)
+ mm_sms_part_set_text (part, text);
+ else if (data) {
+ GByteArray *part_data;
+
+ part_data = g_byte_array_sized_new (data_len);
+ g_byte_array_append (part_data, data, data_len);
+ mm_sms_part_take_data (part, part_data);
+ } else
+ g_assert_not_reached ();
+ mm_sms_part_set_encoding (part, data ? MM_SMS_ENCODING_8BIT : MM_SMS_ENCODING_UNKNOWN);
+ mm_sms_part_set_number (part, mm_gdbus_sms_get_number (MM_GDBUS_SMS (self)));
+
+ /* If creating a CDMA SMS part but we don't have a Teleservice ID, we default to WMT */
+ if (mm_gdbus_sms_get_teleservice_id (MM_GDBUS_SMS (self)) == MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN) {
+ mm_dbg ("Defaulting to WMT teleservice ID when creating SMS part");
+ mm_sms_part_set_cdma_teleservice_id (part, MM_SMS_CDMA_TELESERVICE_ID_WMT);
+ } else
+ mm_sms_part_set_cdma_teleservice_id (part, mm_gdbus_sms_get_teleservice_id (MM_GDBUS_SMS (self)));
+
+ mm_sms_part_set_cdma_service_category (part, mm_gdbus_sms_get_service_category (MM_GDBUS_SMS (self)));
+
+ mm_dbg ("Created SMS part for CDMA SMS");
+
+ /* Add to the list of parts */
+ self->priv->parts = g_list_append (self->priv->parts, part);
+
+ /* No more parts are expected */
+ self->priv->is_assembled = TRUE;
+
+ return TRUE;
+}
+
+static gboolean
+generate_submit_pdus (MMSms *self,
+ GError **error)
+{
+ MMBaseModem *modem;
+ gboolean is_3gpp;
+
+ /* First; decide which kind of PDU we'll generate, based on the current modem caps */
+
+ g_object_get (self,
+ MM_SMS_MODEM, &modem,
+ NULL);
+ g_assert (modem != NULL);
+
+ is_3gpp = mm_iface_modem_is_3gpp (MM_IFACE_MODEM (modem));
+ g_object_unref (modem);
+
+ /* On a 3GPP-capable modem, create always a 3GPP SMS (even if the modem is 3GPP+3GPP2) */
+ if (is_3gpp)
+ return generate_3gpp_submit_pdus (self, error);
+
+ /* Otherwise, create a 3GPP2 SMS */
+ return generate_cdma_submit_pdus (self, error);
+}
+
/*****************************************************************************/
/* Store SMS (DBus call handling) */
@@ -257,9 +342,12 @@ handle_store_ready (MMSms *self,
{
GError *error = NULL;
- if (!MM_SMS_GET_CLASS (self)->store_finish (self, res, &error))
+ if (!MM_SMS_GET_CLASS (self)->store_finish (self, res, &error)) {
+ /* On error, clear up the parts we generated */
+ g_list_free_full (self->priv->parts, (GDestroyNotify)mm_sms_part_free);
+ self->priv->parts = NULL;
g_dbus_method_invocation_take_error (ctx->invocation, error);
- else {
+ } else {
mm_gdbus_sms_set_storage (MM_GDBUS_SMS (ctx->self), ctx->storage);
/* Transition from Unknown->Stored for SMS which were created by the user */
@@ -297,10 +385,12 @@ prepare_sms_to_be_stored (MMSms *self,
/* If the message is a multipart message, we need to set a proper
* multipart reference. When sending a message which wasn't stored
* yet, we can just get a random multipart reference. */
- self->priv->multipart_reference = reference;
- for (l = self->priv->parts; l; l = g_list_next (l)) {
- mm_sms_part_set_concat_reference ((MMSmsPart *)l->data,
- self->priv->multipart_reference);
+ if (self->priv->is_multipart) {
+ self->priv->multipart_reference = reference;
+ for (l = self->priv->parts; l; l = g_list_next (l)) {
+ mm_sms_part_set_concat_reference ((MMSmsPart *)l->data,
+ self->priv->multipart_reference);
+ }
}
return TRUE;
@@ -426,9 +516,12 @@ handle_send_ready (MMSms *self,
{
GError *error = NULL;
- if (!MM_SMS_GET_CLASS (self)->send_finish (self, res, &error))
+ if (!MM_SMS_GET_CLASS (self)->send_finish (self, res, &error)) {
+ /* On error, clear up the parts we generated */
+ g_list_free_full (self->priv->parts, (GDestroyNotify)mm_sms_part_free);
+ self->priv->parts = NULL;
g_dbus_method_invocation_take_error (ctx->invocation, error);
- else {
+ } else {
/* Transition from Unknown->Sent or Stored->Sent */
if (mm_gdbus_sms_get_state (MM_GDBUS_SMS (ctx->self)) == MM_SMS_STATE_UNKNOWN ||
mm_gdbus_sms_get_state (MM_GDBUS_SMS (ctx->self)) == MM_SMS_STATE_STORED) {
@@ -464,10 +557,12 @@ prepare_sms_to_be_sent (MMSms *self,
/* If the message is a multipart message, we need to set a proper
* multipart reference. When sending a message which wasn't stored
* yet, we can just get a random multipart reference. */
- self->priv->multipart_reference = g_random_int_range (1,255);
- for (l = self->priv->parts; l; l = g_list_next (l)) {
- mm_sms_part_set_concat_reference ((MMSmsPart *)l->data,
- self->priv->multipart_reference);
+ if (self->priv->is_multipart) {
+ self->priv->multipart_reference = g_random_int_range (1,255);
+ for (l = self->priv->parts; l; l = g_list_next (l)) {
+ mm_sms_part_set_concat_reference ((MMSmsPart *)l->data,
+ self->priv->multipart_reference);
+ }
}
return TRUE;
@@ -703,7 +798,7 @@ sms_get_store_or_send_command (MMSmsPart *part,
/* AT+CMGW=<length>[, <stat>]<CR> PDU can be entered. <CTRL-Z>/<ESC> */
- pdu = mm_sms_part_get_submit_pdu (part, &pdulen, &msgstart, error);
+ pdu = mm_sms_part_3gpp_get_submit_pdu (part, &pdulen, &msgstart, error);
if (!pdu)
/* 'error' should already be set */
return FALSE;
@@ -1499,6 +1594,8 @@ assemble_sms (MMSms *self,
g_byte_array_ref (fulldata)),
"smsc", mm_sms_part_get_smsc (sorted_parts[0]),
"class", mm_sms_part_get_class (sorted_parts[0]),
+ "teleservice-id", mm_sms_part_get_cdma_teleservice_id (sorted_parts[0]),
+ "service-category", mm_sms_part_get_cdma_service_category (sorted_parts[0]),
"number", mm_sms_part_get_number (sorted_parts[0]),
"validity", (validity_relative ?
g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_RELATIVE, g_variant_new_uint32 (validity_relative)) :
@@ -1725,7 +1822,9 @@ mm_sms_new_from_properties (MMBaseModem *modem,
"state", MM_SMS_STATE_UNKNOWN,
"storage", MM_SMS_STORAGE_UNKNOWN,
"number", mm_sms_properties_get_number (properties),
- "pdu-type", MM_SMS_PDU_TYPE_SUBMIT,
+ "pdu-type", (mm_sms_properties_get_teleservice_id (properties) == MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN ?
+ MM_SMS_PDU_TYPE_SUBMIT :
+ MM_SMS_PDU_TYPE_CDMA_SUBMIT),
"text", text,
"data", (data ?
g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
@@ -1737,7 +1836,10 @@ mm_sms_new_from_properties (MMBaseModem *modem,
NULL),
"smsc", mm_sms_properties_get_smsc (properties),
"class", mm_sms_properties_get_class (properties),
+ "teleservice-id", mm_sms_properties_get_teleservice_id (properties),
+ "service-category", mm_sms_properties_get_service_category (properties),
"delivery-report-request", mm_sms_properties_get_delivery_report_request (properties),
+ "delivery-state", MM_SMS_DELIVERY_STATE_UNKNOWN,
"validity", (mm_sms_properties_get_validity_type (properties) == MM_SMS_VALIDITY_TYPE_RELATIVE ?
g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_RELATIVE, g_variant_new_uint32 (mm_sms_properties_get_validity_relative (properties))) :
g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_UNKNOWN, g_variant_new_boolean (FALSE))),
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index dc58366..2d13f25 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -5,7 +5,8 @@ noinst_PROGRAMS = \
test-charsets \
test-qcdm-serial-port \
test-at-serial-port \
- test-sms-part
+ test-sms-part-3gpp \
+ test-sms-part-cdma
if WITH_QMI
noinst_PROGRAMS += test-modem-helpers-qmi
@@ -139,10 +140,10 @@ endif
################
-test_sms_part_SOURCES = \
- test-sms-part.c
+test_sms_part_3gpp_SOURCES = \
+ test-sms-part-3gpp.c
-test_sms_part_CPPFLAGS = \
+test_sms_part_3gpp_CPPFLAGS = \
$(MM_CFLAGS) \
-I$(top_srcdir) \
-I$(top_srcdir)/src \
@@ -152,11 +153,35 @@ test_sms_part_CPPFLAGS = \
-I$(top_srcdir)/libmm-glib/generated \
-I$(top_builddir)/libmm-glib/generated
-test_sms_part_LDADD = \
+test_sms_part_3gpp_LDADD = \
$(top_builddir)/src/libmodem-helpers.la \
$(MM_LIBS)
if WITH_QMI
-test_sms_part_CPPFLAGS += $(QMI_CFLAGS)
-test_sms_part_LDADD += $(QMI_LIBS)
+test_sms_part_3gpp_CPPFLAGS += $(QMI_CFLAGS)
+test_sms_part_3gpp_LDADD += $(QMI_LIBS)
+endif
+
+################
+
+test_sms_part_cdma_SOURCES = \
+ test-sms-part-cdma.c
+
+test_sms_part_cdma_CPPFLAGS = \
+ $(MM_CFLAGS) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/include \
+ -I$(top_builddir)/include \
+ -I$(top_srcdir)/libmm-glib \
+ -I$(top_srcdir)/libmm-glib/generated \
+ -I$(top_builddir)/libmm-glib/generated
+
+test_sms_part_cdma_LDADD = \
+ $(top_builddir)/src/libmodem-helpers.la \
+ $(MM_LIBS)
+
+if WITH_QMI
+test_sms_part_cdma_CPPFLAGS += $(QMI_CFLAGS)
+test_sms_part_cdma_LDADD += $(QMI_LIBS)
endif
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index 0fdf026..e22942b 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2013 Free Software Foundation, Inc.
@@ -82,7 +82,8 @@ DIST_COMMON = $(top_srcdir)/gtester.make $(srcdir)/Makefile.in \
$(srcdir)/Makefile.am $(top_srcdir)/depcomp
noinst_PROGRAMS = test-modem-helpers$(EXEEXT) test-charsets$(EXEEXT) \
test-qcdm-serial-port$(EXEEXT) test-at-serial-port$(EXEEXT) \
- test-sms-part$(EXEEXT) $(am__EXEEXT_1)
+ test-sms-part-3gpp$(EXEEXT) test-sms-part-cdma$(EXEEXT) \
+ $(am__EXEEXT_1)
@WITH_QMI_TRUE@am__append_1 = test-modem-helpers-qmi
@WITH_QMI_TRUE@am__append_2 = $(QMI_CFLAGS)
@WITH_QMI_TRUE@am__append_3 = $(QMI_LIBS)
@@ -94,17 +95,20 @@ noinst_PROGRAMS = test-modem-helpers$(EXEEXT) test-charsets$(EXEEXT) \
@WITH_QMI_TRUE@am__append_9 = $(QMI_LIBS)
@WITH_QMI_TRUE@am__append_10 = $(QMI_CFLAGS)
@WITH_QMI_TRUE@am__append_11 = $(QMI_LIBS)
+@WITH_QMI_TRUE@am__append_12 = $(QMI_CFLAGS)
+@WITH_QMI_TRUE@am__append_13 = $(QMI_LIBS)
subdir = src/tests
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
$(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/gtk-doc.m4 \
$(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \
- $(top_srcdir)/m4/intltool.m4 $(top_srcdir)/m4/lib-ld.m4 \
- $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
- $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
- $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
- $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
- $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/m4/intltool.m4 $(top_srcdir)/m4/introspection.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/vapigen.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
@@ -149,10 +153,18 @@ test_qcdm_serial_port_DEPENDENCIES = $(am__DEPENDENCIES_1) \
$(top_builddir)/src/libserial.la \
$(top_builddir)/src/libmodem-helpers.la \
$(top_builddir)/libqcdm/src/libqcdm.la $(am__DEPENDENCIES_2)
-am_test_sms_part_OBJECTS = test_sms_part-test-sms-part.$(OBJEXT)
-test_sms_part_OBJECTS = $(am_test_sms_part_OBJECTS)
-test_sms_part_DEPENDENCIES = $(top_builddir)/src/libmodem-helpers.la \
- $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2)
+am_test_sms_part_3gpp_OBJECTS = \
+ test_sms_part_3gpp-test-sms-part-3gpp.$(OBJEXT)
+test_sms_part_3gpp_OBJECTS = $(am_test_sms_part_3gpp_OBJECTS)
+test_sms_part_3gpp_DEPENDENCIES = \
+ $(top_builddir)/src/libmodem-helpers.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_2)
+am_test_sms_part_cdma_OBJECTS = \
+ test_sms_part_cdma-test-sms-part-cdma.$(OBJEXT)
+test_sms_part_cdma_OBJECTS = $(am_test_sms_part_cdma_OBJECTS)
+test_sms_part_cdma_DEPENDENCIES = \
+ $(top_builddir)/src/libmodem-helpers.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_2)
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
@@ -190,11 +202,13 @@ am__v_CCLD_1 =
SOURCES = $(test_at_serial_port_SOURCES) $(test_charsets_SOURCES) \
$(test_modem_helpers_SOURCES) \
$(test_modem_helpers_qmi_SOURCES) \
- $(test_qcdm_serial_port_SOURCES) $(test_sms_part_SOURCES)
+ $(test_qcdm_serial_port_SOURCES) $(test_sms_part_3gpp_SOURCES) \
+ $(test_sms_part_cdma_SOURCES)
DIST_SOURCES = $(test_at_serial_port_SOURCES) $(test_charsets_SOURCES) \
$(test_modem_helpers_SOURCES) \
$(am__test_modem_helpers_qmi_SOURCES_DIST) \
- $(test_qcdm_serial_port_SOURCES) $(test_sms_part_SOURCES)
+ $(test_qcdm_serial_port_SOURCES) $(test_sms_part_3gpp_SOURCES) \
+ $(test_sms_part_cdma_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
@@ -278,6 +292,14 @@ INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@
+INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@
+INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@
+INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@
+INTROSPECTION_LIBS = @INTROSPECTION_LIBS@
+INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
+INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
+INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBICONV = @LIBICONV@
@@ -344,6 +366,9 @@ STRIP = @STRIP@
SYSTEMD_UNIT_DIR = @SYSTEMD_UNIT_DIR@
UDEV_BASE_DIR = @UDEV_BASE_DIR@
USE_NLS = @USE_NLS@
+VAPIGEN = @VAPIGEN@
+VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@
+VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@
VERSION = @VERSION@
XGETTEXT = @XGETTEXT@
XGETTEXT_015 = @XGETTEXT_015@
@@ -482,16 +507,28 @@ test_at_serial_port_LDADD = $(MM_LIBS) \
$(top_builddir)/src/libmodem-helpers.la -lutil $(am__append_9)
################
-test_sms_part_SOURCES = \
- test-sms-part.c
+test_sms_part_3gpp_SOURCES = \
+ test-sms-part-3gpp.c
-test_sms_part_CPPFLAGS = $(MM_CFLAGS) -I$(top_srcdir) \
+test_sms_part_3gpp_CPPFLAGS = $(MM_CFLAGS) -I$(top_srcdir) \
-I$(top_srcdir)/src -I$(top_srcdir)/include \
-I$(top_builddir)/include -I$(top_srcdir)/libmm-glib \
-I$(top_srcdir)/libmm-glib/generated \
-I$(top_builddir)/libmm-glib/generated $(am__append_10)
-test_sms_part_LDADD = $(top_builddir)/src/libmodem-helpers.la \
+test_sms_part_3gpp_LDADD = $(top_builddir)/src/libmodem-helpers.la \
$(MM_LIBS) $(am__append_11)
+
+################
+test_sms_part_cdma_SOURCES = \
+ test-sms-part-cdma.c
+
+test_sms_part_cdma_CPPFLAGS = $(MM_CFLAGS) -I$(top_srcdir) \
+ -I$(top_srcdir)/src -I$(top_srcdir)/include \
+ -I$(top_builddir)/include -I$(top_srcdir)/libmm-glib \
+ -I$(top_srcdir)/libmm-glib/generated \
+ -I$(top_builddir)/libmm-glib/generated $(am__append_12)
+test_sms_part_cdma_LDADD = $(top_builddir)/src/libmodem-helpers.la \
+ $(MM_LIBS) $(am__append_13)
all: all-am
.SUFFIXES:
@@ -557,9 +594,13 @@ test-qcdm-serial-port$(EXEEXT): $(test_qcdm_serial_port_OBJECTS) $(test_qcdm_ser
@rm -f test-qcdm-serial-port$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(test_qcdm_serial_port_OBJECTS) $(test_qcdm_serial_port_LDADD) $(LIBS)
-test-sms-part$(EXEEXT): $(test_sms_part_OBJECTS) $(test_sms_part_DEPENDENCIES) $(EXTRA_test_sms_part_DEPENDENCIES)
- @rm -f test-sms-part$(EXEEXT)
- $(AM_V_CCLD)$(LINK) $(test_sms_part_OBJECTS) $(test_sms_part_LDADD) $(LIBS)
+test-sms-part-3gpp$(EXEEXT): $(test_sms_part_3gpp_OBJECTS) $(test_sms_part_3gpp_DEPENDENCIES) $(EXTRA_test_sms_part_3gpp_DEPENDENCIES)
+ @rm -f test-sms-part-3gpp$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_sms_part_3gpp_OBJECTS) $(test_sms_part_3gpp_LDADD) $(LIBS)
+
+test-sms-part-cdma$(EXEEXT): $(test_sms_part_cdma_OBJECTS) $(test_sms_part_cdma_DEPENDENCIES) $(EXTRA_test_sms_part_cdma_DEPENDENCIES)
+ @rm -f test-sms-part-cdma$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_sms_part_cdma_OBJECTS) $(test_sms_part_cdma_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -572,7 +613,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_modem_helpers-test-modem-helpers.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_modem_helpers_qmi-test-modem-helpers-qmi.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_qcdm_serial_port-test-qcdm-serial-port.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_sms_part-test-sms-part.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_sms_part_3gpp-test-sms-part-3gpp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_sms_part_cdma-test-sms-part-cdma.Po@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
@@ -668,19 +710,33 @@ test_qcdm_serial_port-test-qcdm-serial-port.obj: test-qcdm-serial-port.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_qcdm_serial_port_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_qcdm_serial_port-test-qcdm-serial-port.obj `if test -f 'test-qcdm-serial-port.c'; then $(CYGPATH_W) 'test-qcdm-serial-port.c'; else $(CYGPATH_W) '$(srcdir)/test-qcdm-serial-port.c'; fi`
-test_sms_part-test-sms-part.o: test-sms-part.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_sms_part_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_sms_part-test-sms-part.o -MD -MP -MF $(DEPDIR)/test_sms_part-test-sms-part.Tpo -c -o test_sms_part-test-sms-part.o `test -f 'test-sms-part.c' || echo '$(srcdir)/'`test-sms-part.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_sms_part-test-sms-part.Tpo $(DEPDIR)/test_sms_part-test-sms-part.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-sms-part.c' object='test_sms_part-test-sms-part.o' libtool=no @AMDEPBACKSLASH@
+test_sms_part_3gpp-test-sms-part-3gpp.o: test-sms-part-3gpp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_sms_part_3gpp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_sms_part_3gpp-test-sms-part-3gpp.o -MD -MP -MF $(DEPDIR)/test_sms_part_3gpp-test-sms-part-3gpp.Tpo -c -o test_sms_part_3gpp-test-sms-part-3gpp.o `test -f 'test-sms-part-3gpp.c' || echo '$(srcdir)/'`test-sms-part-3gpp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_sms_part_3gpp-test-sms-part-3gpp.Tpo $(DEPDIR)/test_sms_part_3gpp-test-sms-part-3gpp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-sms-part-3gpp.c' object='test_sms_part_3gpp-test-sms-part-3gpp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_sms_part_3gpp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_sms_part_3gpp-test-sms-part-3gpp.o `test -f 'test-sms-part-3gpp.c' || echo '$(srcdir)/'`test-sms-part-3gpp.c
+
+test_sms_part_3gpp-test-sms-part-3gpp.obj: test-sms-part-3gpp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_sms_part_3gpp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_sms_part_3gpp-test-sms-part-3gpp.obj -MD -MP -MF $(DEPDIR)/test_sms_part_3gpp-test-sms-part-3gpp.Tpo -c -o test_sms_part_3gpp-test-sms-part-3gpp.obj `if test -f 'test-sms-part-3gpp.c'; then $(CYGPATH_W) 'test-sms-part-3gpp.c'; else $(CYGPATH_W) '$(srcdir)/test-sms-part-3gpp.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_sms_part_3gpp-test-sms-part-3gpp.Tpo $(DEPDIR)/test_sms_part_3gpp-test-sms-part-3gpp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-sms-part-3gpp.c' object='test_sms_part_3gpp-test-sms-part-3gpp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_sms_part_3gpp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_sms_part_3gpp-test-sms-part-3gpp.obj `if test -f 'test-sms-part-3gpp.c'; then $(CYGPATH_W) 'test-sms-part-3gpp.c'; else $(CYGPATH_W) '$(srcdir)/test-sms-part-3gpp.c'; fi`
+
+test_sms_part_cdma-test-sms-part-cdma.o: test-sms-part-cdma.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_sms_part_cdma_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_sms_part_cdma-test-sms-part-cdma.o -MD -MP -MF $(DEPDIR)/test_sms_part_cdma-test-sms-part-cdma.Tpo -c -o test_sms_part_cdma-test-sms-part-cdma.o `test -f 'test-sms-part-cdma.c' || echo '$(srcdir)/'`test-sms-part-cdma.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_sms_part_cdma-test-sms-part-cdma.Tpo $(DEPDIR)/test_sms_part_cdma-test-sms-part-cdma.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-sms-part-cdma.c' object='test_sms_part_cdma-test-sms-part-cdma.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_sms_part_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_sms_part-test-sms-part.o `test -f 'test-sms-part.c' || echo '$(srcdir)/'`test-sms-part.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_sms_part_cdma_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_sms_part_cdma-test-sms-part-cdma.o `test -f 'test-sms-part-cdma.c' || echo '$(srcdir)/'`test-sms-part-cdma.c
-test_sms_part-test-sms-part.obj: test-sms-part.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_sms_part_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_sms_part-test-sms-part.obj -MD -MP -MF $(DEPDIR)/test_sms_part-test-sms-part.Tpo -c -o test_sms_part-test-sms-part.obj `if test -f 'test-sms-part.c'; then $(CYGPATH_W) 'test-sms-part.c'; else $(CYGPATH_W) '$(srcdir)/test-sms-part.c'; fi`
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_sms_part-test-sms-part.Tpo $(DEPDIR)/test_sms_part-test-sms-part.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-sms-part.c' object='test_sms_part-test-sms-part.obj' libtool=no @AMDEPBACKSLASH@
+test_sms_part_cdma-test-sms-part-cdma.obj: test-sms-part-cdma.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_sms_part_cdma_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_sms_part_cdma-test-sms-part-cdma.obj -MD -MP -MF $(DEPDIR)/test_sms_part_cdma-test-sms-part-cdma.Tpo -c -o test_sms_part_cdma-test-sms-part-cdma.obj `if test -f 'test-sms-part-cdma.c'; then $(CYGPATH_W) 'test-sms-part-cdma.c'; else $(CYGPATH_W) '$(srcdir)/test-sms-part-cdma.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_sms_part_cdma-test-sms-part-cdma.Tpo $(DEPDIR)/test_sms_part_cdma-test-sms-part-cdma.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-sms-part-cdma.c' object='test_sms_part_cdma-test-sms-part-cdma.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_sms_part_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_sms_part-test-sms-part.obj `if test -f 'test-sms-part.c'; then $(CYGPATH_W) 'test-sms-part.c'; else $(CYGPATH_W) '$(srcdir)/test-sms-part.c'; fi`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_sms_part_cdma_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_sms_part_cdma-test-sms-part-cdma.obj `if test -f 'test-sms-part-cdma.c'; then $(CYGPATH_W) 'test-sms-part-cdma.c'; else $(CYGPATH_W) '$(srcdir)/test-sms-part-cdma.c'; fi`
mostlyclean-libtool:
-rm -f *.lo
diff --git a/src/tests/test-at-serial-port.c b/src/tests/test-at-serial-port.c
index 1e7ff45..0b5f506 100644
--- a/src/tests/test-at-serial-port.c
+++ b/src/tests/test-at-serial-port.c
@@ -71,7 +71,17 @@ _mm_log (const char *loc,
const char *fmt,
...)
{
+#if defined ENABLE_TEST_MESSAGE_TRACES
/* Dummy log function */
+ va_list args;
+ gchar *msg;
+
+ va_start (args, fmt);
+ msg = g_strdup_vprintf (fmt, args);
+ va_end (args);
+ g_print ("%s\n", msg);
+ g_free (msg);
+#endif
}
int main (int argc, char **argv)
diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c
index 5d94571..b4ff8a2 100644
--- a/src/tests/test-modem-helpers.c
+++ b/src/tests/test-modem-helpers.c
@@ -840,12 +840,52 @@ test_creg2_iridium_solicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "+CREG:002,001,\"18d8\",\"ffff\"";
- const CregResult result = { 1, 0x18D8, 0xFFFF, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 4, FALSE, FALSE };
+ const CregResult result = { 1, 0x18D8, 0xFFFF, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 5, FALSE, FALSE };
test_creg_match ("Iridium, CREG=2", TRUE, reply, data, &result);
}
static void
+test_creg2_no_leading_zeros_solicited (void *f, gpointer d)
+{
+ RegTestData *data = (RegTestData *) d;
+ const char *reply = "+CREG:2,1,0001,0010";
+ const CregResult result = { 1, 0x0001, 0x0010, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 4, FALSE, FALSE };
+
+ test_creg_match ("solicited CREG=2 with no leading zeros in integer fields", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_leading_zeros_solicited (void *f, gpointer d)
+{
+ RegTestData *data = (RegTestData *) d;
+ const char *reply = "+CREG:002,001,\"0001\",\"0010\"";
+ const CregResult result = { 1, 0x0001, 0x0010, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 5, FALSE, FALSE };
+
+ test_creg_match ("solicited CREG=2 with leading zeros in integer fields", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_no_leading_zeros_unsolicited (void *f, gpointer d)
+{
+ RegTestData *data = (RegTestData *) d;
+ const char *reply = "\r\n+CREG: 1,0001,0010,0\r\n";
+ const CregResult result = { 1, 0x0001, 0x0010, MM_MODEM_ACCESS_TECHNOLOGY_GSM, 6, FALSE, FALSE };
+
+ test_creg_match ("unsolicited CREG=2 with no leading zeros in integer fields", FALSE, reply, data, &result);
+}
+
+static void
+test_creg2_leading_zeros_unsolicited (void *f, gpointer d)
+{
+ RegTestData *data = (RegTestData *) d;
+ const char *reply = "\r\n+CREG: 001,\"0001\",\"0010\",000\r\n";
+ const CregResult result = { 1, 0x0001, 0x0010, MM_MODEM_ACCESS_TECHNOLOGY_GSM, 7, FALSE, FALSE };
+
+ test_creg_match ("unsolicited CREG=2 with leading zeros in integer fields", FALSE, reply, data, &result);
+}
+
+static void
test_cgreg1_solicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
@@ -870,7 +910,7 @@ test_cgreg2_f3607gw_solicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "+CGREG: 2,1,\"8BE3\",\"00002B5D\",3";
- const CregResult result = { 1, 0x8BE3, 0x2B5D, MM_MODEM_ACCESS_TECHNOLOGY_EDGE , 6, TRUE, FALSE };
+ const CregResult result = { 1, 0x8BE3, 0x2B5D, MM_MODEM_ACCESS_TECHNOLOGY_EDGE, 8, TRUE, FALSE };
test_creg_match ("Ericsson F3607gw CGREG=2", TRUE, reply, data, &result);
}
@@ -880,7 +920,7 @@ test_cgreg2_f3607gw_unsolicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CGREG: 1,\"8BE3\",\"00002B5D\",3\r\n";
- const CregResult result = { 1, 0x8BE3, 0x2B5D, MM_MODEM_ACCESS_TECHNOLOGY_EDGE , 5, TRUE, FALSE };
+ const CregResult result = { 1, 0x8BE3, 0x2B5D, MM_MODEM_ACCESS_TECHNOLOGY_EDGE, 6, TRUE, FALSE };
test_creg_match ("Ericsson F3607gw CGREG=2", FALSE, reply, data, &result);
}
@@ -900,7 +940,7 @@ test_cgreg2_md400_unsolicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CGREG: 5,\"0502\",\"0404736D\",2\r\n";
- const CregResult result = { 5, 0x0502, 0x0404736D, MM_MODEM_ACCESS_TECHNOLOGY_UMTS, 5, TRUE, FALSE };
+ const CregResult result = { 5, 0x0502, 0x0404736D, MM_MODEM_ACCESS_TECHNOLOGY_UMTS, 6, TRUE, FALSE };
test_creg_match ("Sony-Ericsson MD400 CGREG=2", FALSE, reply, data, &result);
}
@@ -941,7 +981,7 @@ test_creg2_s8500_wave_unsolicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CREG: 2,1,000B,2816, B, C2816\r\n";
- const CregResult result = { 1, 0x000B, 0x2816, MM_MODEM_ACCESS_TECHNOLOGY_GSM, 7, FALSE, FALSE };
+ const CregResult result = { 1, 0x000B, 0x2816, MM_MODEM_ACCESS_TECHNOLOGY_GSM, 9, FALSE, FALSE };
test_creg_match ("Samsung Wave S8500 CREG=2", FALSE, reply, data, &result);
}
@@ -961,7 +1001,7 @@ test_cgreg2_unsolicited_with_rac (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CGREG: 1,\"1422\",\"00000142\",3,\"00\"\r\n";
- const CregResult result = { 1, 0x1422, 0x0142, MM_MODEM_ACCESS_TECHNOLOGY_EDGE, 8, TRUE, FALSE };
+ const CregResult result = { 1, 0x1422, 0x0142, MM_MODEM_ACCESS_TECHNOLOGY_EDGE, 10, TRUE, FALSE };
test_creg_match ("CGREG=2 with RAC", FALSE, reply, data, &result);
}
@@ -991,7 +1031,7 @@ test_cereg2_solicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CEREG: 2,1, 1F00, 79D903 ,7\r\n";
- const CregResult result = { 1, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 6, FALSE, TRUE };
+ const CregResult result = { 1, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 8, FALSE, TRUE };
test_creg_match ("CEREG=2", TRUE, reply, data, &result);
}
@@ -1001,17 +1041,37 @@ test_cereg2_unsolicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CEREG: 1, 1F00, 79D903 ,7\r\n";
- const CregResult result = { 1, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 5, FALSE, TRUE };
+ const CregResult result = { 1, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 6, FALSE, TRUE };
test_creg_match ("CEREG=2", FALSE, reply, data, &result);
}
static void
+test_cereg2_altair_lte_solicited (void *f, gpointer d)
+{
+ RegTestData *data = (RegTestData *) d;
+ const char *reply = "\r\n+CEREG: 1, 2, 0001, 00000100, 7\r\n";
+ const CregResult result = { 2, 0x0001, 0x00000100, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 8, FALSE, TRUE };
+
+ test_creg_match ("Altair LTE CEREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_cereg2_altair_lte_unsolicited (void *f, gpointer d)
+{
+ RegTestData *data = (RegTestData *) d;
+ const char *reply = "\r\n+CEREG: 2, 0001, 00000100, 7\r\n";
+ const CregResult result = { 2, 0x0001, 0x00000100, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 6, FALSE, TRUE };
+
+ test_creg_match ("Altair LTE CEREG=2", FALSE, reply, data, &result);
+}
+
+static void
test_cereg2_novatel_lte_solicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CEREG: 2,1, 1F00, 20 ,79D903 ,7\r\n";
- const CregResult result = { 1, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 10, FALSE, TRUE };
+ const CregResult result = { 1, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 12, FALSE, TRUE };
test_creg_match ("Novatel LTE E362 CEREG=2", TRUE, reply, data, &result);
}
@@ -1021,7 +1081,7 @@ test_cereg2_novatel_lte_unsolicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CEREG: 1, 1F00, 20 ,79D903 ,7\r\n";
- const CregResult result = { 1, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 9, FALSE, TRUE };
+ const CregResult result = { 1, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 11, FALSE, TRUE };
test_creg_match ("Novatel LTE E362 CEREG=2", FALSE, reply, data, &result);
}
@@ -1463,6 +1523,105 @@ test_cind_response_moto_v3m (void *f, gpointer d)
}
/*****************************************************************************/
+/* Test ICCID parsing */
+
+static void
+test_iccid_parse_quoted_swap_19_digit (void *f, gpointer d)
+{
+ const char *raw_iccid = "\"984402003576012594F9\"";
+ const char *expected = "8944200053671052499";
+ char *parsed;
+ GError *error = NULL;
+
+ parsed = mm_3gpp_parse_iccid (raw_iccid, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (parsed, ==, expected);
+}
+
+static void
+test_iccid_parse_unquoted_swap_20_digit (void *f, gpointer d)
+{
+ const char *raw_iccid = "98231420326409614067";
+ const char *expected = "89324102234690160476";
+ char *parsed;
+ GError *error = NULL;
+
+ parsed = mm_3gpp_parse_iccid (raw_iccid, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (parsed, ==, expected);
+}
+
+static void
+test_iccid_parse_unquoted_unswapped_19_digit (void *f, gpointer d)
+{
+ const char *raw_iccid = "8944200053671052499F";
+ const char *expected = "8944200053671052499";
+ char *parsed;
+ GError *error = NULL;
+
+ parsed = mm_3gpp_parse_iccid (raw_iccid, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (parsed, ==, expected);
+}
+
+static void
+test_iccid_parse_quoted_unswapped_20_digit (void *f, gpointer d)
+{
+ const char *raw_iccid = "\"89324102234690160476\"";
+ const char *expected = "89324102234690160476";
+ char *parsed;
+ GError *error = NULL;
+
+ parsed = mm_3gpp_parse_iccid (raw_iccid, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (parsed, ==, expected);
+}
+
+static void
+test_iccid_parse_short (void *f, gpointer d)
+{
+ const char *raw_iccid = "982314203264096";
+ char *parsed;
+ GError *error = NULL;
+
+ parsed = mm_3gpp_parse_iccid (raw_iccid, &error);
+ g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED);
+}
+
+static void
+test_iccid_parse_invalid_chars (void *f, gpointer d)
+{
+ const char *raw_iccid = "98231420326ab9614067";
+ char *parsed;
+ GError *error = NULL;
+
+ parsed = mm_3gpp_parse_iccid (raw_iccid, &error);
+ g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED);
+}
+
+static void
+test_iccid_parse_quoted_invalid_mii (void *f, gpointer d)
+{
+ const char *raw_iccid = "\"0044200053671052499\"";
+ char *parsed;
+ GError *error = NULL;
+
+ parsed = mm_3gpp_parse_iccid (raw_iccid, &error);
+ g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED);
+}
+
+static void
+test_iccid_parse_unquoted_invalid_mii (void *f, gpointer d)
+{
+ const char *raw_iccid = "0044200053671052499";
+ char *parsed;
+ GError *error = NULL;
+
+ parsed = mm_3gpp_parse_iccid (raw_iccid, &error);
+ g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED);
+}
+
+/*****************************************************************************/
/* Test CGDCONT test responses */
static void
@@ -1548,6 +1707,20 @@ test_cgdcont_test_response_multiple_and_ignore (void *f, gpointer d)
test_cgdcont_test_results ("Multiple and Ignore", reply, &expected[0], G_N_ELEMENTS (expected));
}
+static void
+test_cgdcont_test_response_single_context (void *f, gpointer d)
+{
+ const gchar *reply =
+ "+CGDCONT: (1),\"IP\",,,(0),(0)\r\n"
+ "+CGDCONT: (1),\"IPV6\",,,(0),(0)\r\n";
+ static MM3gppPdpContextFormat expected[] = {
+ { 1, 1, MM_BEARER_IP_FAMILY_IPV4 },
+ { 1, 1, MM_BEARER_IP_FAMILY_IPV6 }
+ };
+
+ test_cgdcont_test_results ("Single Context", reply, &expected[0], G_N_ELEMENTS (expected));
+}
+
/*****************************************************************************/
/* Test CGDCONT read responses */
@@ -2158,7 +2331,17 @@ _mm_log (const char *loc,
const char *fmt,
...)
{
+#if defined ENABLE_TEST_MESSAGE_TRACES
/* Dummy log function */
+ va_list args;
+ gchar *msg;
+
+ va_start (args, fmt);
+ msg = g_strdup_vprintf (fmt, args);
+ va_end (args);
+ g_print ("%s\n", msg);
+ g_free (msg);
+#endif
}
#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (GTestFixtureFunc) t, NULL)
@@ -2222,6 +2405,10 @@ int main (int argc, char **argv)
g_test_suite_add (suite, TESTCASE (test_creg2_s8500_wave_unsolicited, reg_data));
g_test_suite_add (suite, TESTCASE (test_creg2_gobi_weird_solicited, reg_data));
g_test_suite_add (suite, TESTCASE (test_creg2_iridium_solicited, reg_data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_no_leading_zeros_solicited, reg_data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_leading_zeros_solicited, reg_data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_no_leading_zeros_unsolicited, reg_data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_leading_zeros_unsolicited, reg_data));
g_test_suite_add (suite, TESTCASE (test_cgreg1_solicited, reg_data));
g_test_suite_add (suite, TESTCASE (test_cgreg1_unsolicited, reg_data));
@@ -2235,6 +2422,8 @@ int main (int argc, char **argv)
g_test_suite_add (suite, TESTCASE (test_cereg1_unsolicited, reg_data));
g_test_suite_add (suite, TESTCASE (test_cereg2_solicited, reg_data));
g_test_suite_add (suite, TESTCASE (test_cereg2_unsolicited, reg_data));
+ g_test_suite_add (suite, TESTCASE (test_cereg2_altair_lte_solicited, reg_data));
+ g_test_suite_add (suite, TESTCASE (test_cereg2_altair_lte_unsolicited, reg_data));
g_test_suite_add (suite, TESTCASE (test_cereg2_novatel_lte_solicited, reg_data));
g_test_suite_add (suite, TESTCASE (test_cereg2_novatel_lte_unsolicited, reg_data));
@@ -2249,6 +2438,15 @@ int main (int argc, char **argv)
g_test_suite_add (suite, TESTCASE (test_cind_response_linktop_lw273, NULL));
g_test_suite_add (suite, TESTCASE (test_cind_response_moto_v3m, NULL));
+ g_test_suite_add (suite, TESTCASE (test_iccid_parse_quoted_swap_19_digit, NULL));
+ g_test_suite_add (suite, TESTCASE (test_iccid_parse_unquoted_swap_20_digit, NULL));
+ g_test_suite_add (suite, TESTCASE (test_iccid_parse_unquoted_unswapped_19_digit, NULL));
+ g_test_suite_add (suite, TESTCASE (test_iccid_parse_quoted_unswapped_20_digit, NULL));
+ g_test_suite_add (suite, TESTCASE (test_iccid_parse_short, NULL));
+ g_test_suite_add (suite, TESTCASE (test_iccid_parse_invalid_chars, NULL));
+ g_test_suite_add (suite, TESTCASE (test_iccid_parse_quoted_invalid_mii, NULL));
+ g_test_suite_add (suite, TESTCASE (test_iccid_parse_unquoted_invalid_mii, NULL));
+
while (item->devid) {
g_test_suite_add (suite, TESTCASE (test_devid_item, (gconstpointer) item));
item++;
@@ -2259,6 +2457,7 @@ int main (int argc, char **argv)
g_test_suite_add (suite, TESTCASE (test_cgdcont_test_response_single, NULL));
g_test_suite_add (suite, TESTCASE (test_cgdcont_test_response_multiple, NULL));
g_test_suite_add (suite, TESTCASE (test_cgdcont_test_response_multiple_and_ignore, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cgdcont_test_response_single_context, NULL));
g_test_suite_add (suite, TESTCASE (test_cgdcont_read_response_nokia, NULL));
g_test_suite_add (suite, TESTCASE (test_cgdcont_read_response_samsung, NULL));
diff --git a/src/tests/test-qcdm-serial-port.c b/src/tests/test-qcdm-serial-port.c
index 1185838..1875cb5 100644
--- a/src/tests/test-qcdm-serial-port.c
+++ b/src/tests/test-qcdm-serial-port.c
@@ -451,7 +451,17 @@ _mm_log (const char *loc,
const char *fmt,
...)
{
+#if defined ENABLE_TEST_MESSAGE_TRACES
/* Dummy log function */
+ va_list args;
+ gchar *msg;
+
+ va_start (args, fmt);
+ msg = g_strdup_vprintf (fmt, args);
+ va_end (args);
+ g_print ("%s\n", msg);
+ g_free (msg);
+#endif
}
int main (int argc, char **argv)
diff --git a/src/tests/test-sms-part.c b/src/tests/test-sms-part-3gpp.c
index 33287e2..58de8a6 100644
--- a/src/tests/test-sms-part.c
+++ b/src/tests/test-sms-part-3gpp.c
@@ -23,7 +23,7 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-sms-part.h"
+#include "mm-sms-part-3gpp.h"
#include "mm-log.h"
/* If defined will print debugging traces */
@@ -58,7 +58,7 @@ common_test_part_from_hexpdu (const gchar *hexpdu,
MMSmsPart *part;
GError *error = NULL;
- part = mm_sms_part_new_from_pdu (0, hexpdu, &error);
+ part = mm_sms_part_3gpp_new_from_pdu (0, hexpdu, &error);
g_assert_no_error (error);
g_assert (part != NULL);
@@ -356,7 +356,7 @@ test_pdu_insufficient_data (void)
};
hexpdu = mm_utils_bin2hexstr (pdu, sizeof (pdu));
- part = mm_sms_part_new_from_pdu (0, hexpdu, &error);
+ part = mm_sms_part_3gpp_new_from_pdu (0, hexpdu, &error);
g_assert (part == NULL);
/* We don't care for the specific error type */
g_assert (error != NULL);
@@ -464,7 +464,7 @@ common_test_address_encode (const gchar *address,
guint8 buf[20];
gsize enclen;
- enclen = mm_sms_part_encode_address (address, buf, sizeof (buf), smsc);
+ enclen = mm_sms_part_3gpp_encode_address (address, buf, sizeof (buf), smsc);
g_assert_cmpuint (enclen, ==, expected_size);
g_assert_cmpint (memcmp (buf, expected, expected_size), ==, 0);
}
@@ -531,7 +531,7 @@ common_test_create_pdu (const gchar *smsc,
MMSmsEncoding encoding = MM_SMS_ENCODING_UNKNOWN;
/* Detect best encoding */
- mm_sms_part_util_split_text (text, &encoding);
+ mm_sms_part_3gpp_util_split_text (text, &encoding);
mm_sms_part_set_text (part, text);
mm_sms_part_set_encoding (part, encoding);
}
@@ -540,10 +540,10 @@ common_test_create_pdu (const gchar *smsc,
if (class >= 0)
mm_sms_part_set_class (part, class);
- pdu = mm_sms_part_get_submit_pdu (part,
- &len,
- &msgstart,
- &error);
+ pdu = mm_sms_part_3gpp_get_submit_pdu (part,
+ &len,
+ &msgstart,
+ &error);
trace_pdu (pdu, len);
@@ -716,7 +716,7 @@ common_test_text_split (const gchar *text,
MMSmsEncoding out_encoding = MM_SMS_ENCODING_UNKNOWN;
guint i;
- out = mm_sms_part_util_split_text (text, &out_encoding);
+ out = mm_sms_part_3gpp_util_split_text (text, &out_encoding);
g_assert (out != NULL);
g_assert (out_encoding != MM_SMS_ENCODING_UNKNOWN);
@@ -862,39 +862,39 @@ int main (int argc, char **argv)
g_type_init ();
g_test_init (&argc, &argv, NULL);
- g_test_add_func ("/MM/SMS/PDU-Parser/pdu1", test_pdu1);
- g_test_add_func ("/MM/SMS/PDU-Parser/pdu2", test_pdu2);
- g_test_add_func ("/MM/SMS/PDU-Parser/pdu3", test_pdu3);
- g_test_add_func ("/MM/SMS/PDU-Parser/pdu3-nonzero-pid", test_pdu3_nzpid);
- g_test_add_func ("/MM/SMS/PDU-Parser/pdu3-mms", test_pdu3_mms);
- g_test_add_func ("/MM/SMS/PDU-Parser/pdu3-natl", test_pdu3_natl);
- g_test_add_func ("/MM/SMS/PDU-Parser/pdu3-8bit", test_pdu3_8bit);
- g_test_add_func ("/MM/SMS/PDU-Parser/pdu-dcsf1", test_pdu_dcsf1);
- g_test_add_func ("/MM/SMS/PDU-Parser/pdu-dcsf-8bit", test_pdu_dcsf_8bit);
- g_test_add_func ("/MM/SMS/PDU-Parser/pdu-insufficient-data", test_pdu_insufficient_data);
- g_test_add_func ("/MM/SMS/PDU-Parser/pdu-udhi", test_pdu_udhi);
- g_test_add_func ("/MM/SMS/PDU-Parser/pdu-multipart", test_pdu_multipart);
- g_test_add_func ("/MM/SMS/PDU-Parser/pdu-stored-by-us", test_pdu_stored_by_us);
- g_test_add_func ("/MM/SMS/PDU-Parser/pdu-not-stored", test_pdu_not_stored);
-
- g_test_add_func ("/MM/SMS/Address-Encoder/smsc-intl", test_address_encode_smsc_intl);
- g_test_add_func ("/MM/SMS/Address-Encoder/smsc-unknown", test_address_encode_smsc_unknown);
- g_test_add_func ("/MM/SMS/Address-Encoder/intl", test_address_encode_intl);
- g_test_add_func ("/MM/SMS/Address-Encoder/unknown", test_address_encode_unknown);
-
- g_test_add_func ("/MM/SMS/PDU-Creator/UCS2-with-smsc", test_create_pdu_ucs2_with_smsc);
- g_test_add_func ("/MM/SMS/PDU-Creator/UCS2-no-smsc", test_create_pdu_ucs2_no_smsc);
- g_test_add_func ("/MM/SMS/PDU-Creator/GSM-with-smsc", test_create_pdu_gsm_with_smsc);
- g_test_add_func ("/MM/SMS/PDU-Creator/GSM-no-smsc", test_create_pdu_gsm_no_smsc);
- g_test_add_func ("/MM/SMS/PDU-Creator/GSM-3", test_create_pdu_gsm_3);
- g_test_add_func ("/MM/SMS/PDU-Creator/GSM-no-validity", test_create_pdu_gsm_no_validity);
-
- g_test_add_func ("/MM/SMS/Text-Split/short", test_text_split_short);
- g_test_add_func ("/MM/SMS/Text-Split/short-UCS2", test_text_split_short_ucs2);
- g_test_add_func ("/MM/SMS/Text-Split/max-single-pdu", test_text_split_max_single_pdu);
- g_test_add_func ("/MM/SMS/Text-Split/max-single-pdu-UCS2", test_text_split_max_single_pdu_ucs2);
- g_test_add_func ("/MM/SMS/Text-Split/two-pdu", test_text_split_two_pdu);
- g_test_add_func ("/MM/SMS/Text-Split/two-pdu-UCS2", test_text_split_two_pdu_ucs2);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu1", test_pdu1);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu2", test_pdu2);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu3", test_pdu3);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu3-nonzero-pid", test_pdu3_nzpid);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu3-mms", test_pdu3_mms);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu3-natl", test_pdu3_natl);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu3-8bit", test_pdu3_8bit);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-dcsf1", test_pdu_dcsf1);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-dcsf-8bit", test_pdu_dcsf_8bit);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-insufficient-data", test_pdu_insufficient_data);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-udhi", test_pdu_udhi);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-multipart", test_pdu_multipart);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-stored-by-us", test_pdu_stored_by_us);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu-not-stored", test_pdu_not_stored);
+
+ g_test_add_func ("/MM/SMS/3GPP/Address-Encoder/smsc-intl", test_address_encode_smsc_intl);
+ g_test_add_func ("/MM/SMS/3GPP/Address-Encoder/smsc-unknown", test_address_encode_smsc_unknown);
+ g_test_add_func ("/MM/SMS/3GPP/Address-Encoder/intl", test_address_encode_intl);
+ g_test_add_func ("/MM/SMS/3GPP/Address-Encoder/unknown", test_address_encode_unknown);
+
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Creator/UCS2-with-smsc", test_create_pdu_ucs2_with_smsc);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Creator/UCS2-no-smsc", test_create_pdu_ucs2_no_smsc);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Creator/GSM-with-smsc", test_create_pdu_gsm_with_smsc);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Creator/GSM-no-smsc", test_create_pdu_gsm_no_smsc);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Creator/GSM-3", test_create_pdu_gsm_3);
+ g_test_add_func ("/MM/SMS/3GPP/PDU-Creator/GSM-no-validity", test_create_pdu_gsm_no_validity);
+
+ g_test_add_func ("/MM/SMS/3GPP/Text-Split/short", test_text_split_short);
+ g_test_add_func ("/MM/SMS/3GPP/Text-Split/short-UCS2", test_text_split_short_ucs2);
+ g_test_add_func ("/MM/SMS/3GPP/Text-Split/max-single-pdu", test_text_split_max_single_pdu);
+ g_test_add_func ("/MM/SMS/3GPP/Text-Split/max-single-pdu-UCS2", test_text_split_max_single_pdu_ucs2);
+ g_test_add_func ("/MM/SMS/3GPP/Text-Split/two-pdu", test_text_split_two_pdu);
+ g_test_add_func ("/MM/SMS/3GPP/Text-Split/two-pdu-UCS2", test_text_split_two_pdu_ucs2);
return g_test_run ();
}
diff --git a/src/tests/test-sms-part-cdma.c b/src/tests/test-sms-part-cdma.c
new file mode 100644
index 0000000..644de8d
--- /dev/null
+++ b/src/tests/test-sms-part-cdma.c
@@ -0,0 +1,547 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2013 Google, Inc.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <string.h>
+#include <stdio.h>
+#include <locale.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-sms-part-cdma.h"
+#include "mm-log.h"
+
+/* If defined will print debugging traces */
+#ifdef TEST_SMS_PART_ENABLE_TRACE
+#define trace_pdu(pdu, pdu_len) do { \
+ guint i; \
+ \
+ g_print ("\n "); \
+ for (i = 0; i < len; i++) { \
+ g_print (" 0x%02X", pdu[i]); \
+ if (((i + 1) % 12) == 0) \
+ g_print ("\n "); \
+ } \
+ g_print ("\n"); \
+ } while (0)
+#else
+#define trace_pdu(...)
+#endif
+
+/********************* PDU PARSER TESTS *********************/
+
+static void
+common_test_part_from_hexpdu (const gchar *hexpdu,
+ MMSmsCdmaTeleserviceId expected_teleservice_id,
+ MMSmsCdmaServiceCategory expected_service_category,
+ const gchar *expected_address,
+ guint8 expected_bearer_reply_option,
+ const gchar *expected_text)
+{
+ MMSmsPart *part;
+ GError *error = NULL;
+
+ mm_dbg (" ");
+ part = mm_sms_part_cdma_new_from_pdu (0, hexpdu, &error);
+ g_assert_no_error (error);
+ g_assert (part != NULL);
+
+ if (expected_teleservice_id != MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN)
+ g_assert_cmpuint (expected_teleservice_id, ==, mm_sms_part_get_cdma_teleservice_id (part));
+ if (expected_service_category != MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN)
+ g_assert_cmpuint (expected_service_category, ==, mm_sms_part_get_cdma_service_category (part));
+ if (expected_address) {
+ if (expected_address[0])
+ g_assert_cmpstr (expected_address, ==, mm_sms_part_get_number (part));
+ else
+ g_assert (mm_sms_part_get_number (part) == NULL);
+ }
+ if (expected_bearer_reply_option)
+ g_assert_cmpuint (expected_bearer_reply_option, ==, mm_sms_part_get_message_reference (part));
+ if (expected_text)
+ g_assert_cmpstr (expected_text, ==, mm_sms_part_get_text (part));
+
+ mm_sms_part_free (part);
+}
+
+static void
+common_test_part_from_pdu (const guint8 *pdu,
+ gsize pdu_size,
+ MMSmsCdmaTeleserviceId expected_teleservice_id,
+ MMSmsCdmaServiceCategory expected_service_category,
+ const gchar *expected_address,
+ guint8 expected_bearer_reply_option,
+ const gchar *expected_text)
+{
+ gchar *hexpdu;
+
+ hexpdu = mm_utils_bin2hexstr (pdu, pdu_size);
+ common_test_part_from_hexpdu (hexpdu,
+ expected_teleservice_id,
+ expected_service_category,
+ expected_address,
+ expected_bearer_reply_option,
+ expected_text);
+ g_free (hexpdu);
+}
+
+static void
+common_test_invalid_part_from_hexpdu (const gchar *hexpdu)
+{
+ MMSmsPart *part;
+ GError *error = NULL;
+
+ mm_dbg (" ");
+ part = mm_sms_part_cdma_new_from_pdu (0, hexpdu, &error);
+ g_assert (part == NULL);
+ /* We don't care for the specific error type */
+ g_assert (error != NULL);
+ g_error_free (error);
+}
+
+static void
+common_test_invalid_part_from_pdu (const guint8 *pdu,
+ gsize pdu_size)
+{
+ gchar *hexpdu;
+
+ hexpdu = mm_utils_bin2hexstr (pdu, pdu_size);
+ common_test_invalid_part_from_hexpdu (hexpdu);
+ g_free (hexpdu);
+}
+
+static void
+test_pdu1 (void)
+{
+ static const guint8 pdu[] = {
+ /* message type */
+ 0x00,
+ /* teleservice id */
+ 0x00, 0x02,
+ 0x10, 0x02,
+ /* originating address */
+ 0x02, 0x07,
+ 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
+ /* bearer reply option */
+ 0x06, 0x01,
+ 0xFC,
+ /* bearer data */
+ 0x08, 0x15,
+ 0x00, 0x03, 0x16, 0x8D, 0x30, 0x01, 0x06,
+ 0x10, 0x24, 0x18, 0x30, 0x60, 0x80, 0x03,
+ 0x06, 0x10, 0x10, 0x04, 0x04, 0x48, 0x47
+ };
+
+ common_test_part_from_pdu (
+ pdu, sizeof (pdu),
+ MM_SMS_CDMA_TELESERVICE_ID_WMT,
+ MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN,
+ "3305773196",
+ 63,
+ "AAAA");
+}
+
+static void
+test_invalid_parameter_length (void)
+{
+ static const guint8 pdu[] = {
+ /* message type */
+ 0x00,
+ /* teleservice id */
+ 0x00, 0x02,
+ 0x10, 0x02,
+ /* originating address */
+ 0x02, 0x07,
+ 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
+ /* bearer reply option */
+ 0x06, 0x01,
+ 0xFC,
+ /* bearer data */
+ 0x08, 0x20, /* wrong parameter length! */
+ 0x00, 0x03, 0x16, 0x8D, 0x30, 0x01, 0x06,
+ 0x10, 0x24, 0x18, 0x30, 0x60, 0x80, 0x03,
+ 0x06, 0x10, 0x10, 0x04, 0x04, 0x48, 0x47
+ };
+
+ common_test_invalid_part_from_pdu (pdu, sizeof (pdu));
+}
+
+static void
+test_invalid_address_length (void)
+{
+ static const guint8 pdu[] = {
+ /* message type */
+ 0x00,
+ /* teleservice id */
+ 0x00, 0x02,
+ 0x10, 0x02,
+ /* originating address (wrong num_fields) */
+ 0x02, 0x07,
+ 0x03, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
+ /* bearer reply option */
+ 0x06, 0x01,
+ 0xFC,
+ /* bearer data */
+ 0x08, 0x15,
+ 0x00, 0x03, 0x16, 0x8D, 0x30, 0x01, 0x06,
+ 0x10, 0x24, 0x18, 0x30, 0x60, 0x80, 0x03,
+ 0x06, 0x10, 0x10, 0x04, 0x04, 0x48, 0x47
+ };
+
+ common_test_part_from_pdu (
+ pdu, sizeof (pdu),
+ MM_SMS_CDMA_TELESERVICE_ID_WMT,
+ MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN,
+ "",
+ 63,
+ NULL);
+}
+
+static void
+test_created_by_us (void)
+{
+ static const guint8 pdu[] = {
+ /* message type */
+ 0x00,
+ /* teleservice id */
+ 0x00, 0x02,
+ 0x10, 0x02,
+ /* destination address */
+ 0x04, 0x07,
+ 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
+ /* bearer data */
+ 0x08, 0x0D,
+ 0x00, 0x03, 0x20, 0x00, 0x00, /* message id */
+ 0x01, 0x06, 0x10, 0x24, 0x18, 0x30, 0x60, 0x80 /* user_data */
+ };
+
+ common_test_part_from_pdu (
+ pdu, sizeof (pdu),
+ MM_SMS_CDMA_TELESERVICE_ID_WMT,
+ MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN,
+ "3305773196",
+ 0,
+ "AAAA");
+}
+
+static void
+test_latin_encoding (void)
+{
+ static const guint8 pdu[] = {
+ /* message type */
+ 0x00,
+ /* teleservice id */
+ 0x00, 0x02,
+ 0x10, 0x02,
+ /* originating address */
+ 0x02, 0x07,
+ 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
+ /* bearer reply option */
+ 0x06, 0x01,
+ 0xFC,
+ /* bearer data */
+ 0x08, 0x39,
+ /* message id */
+ 0x00, 0x03,
+ 0x13, 0x8D, 0x20,
+ /* user data */
+ 0x01, 0x27,
+ 0x41, 0x29, 0x19, 0x22, 0xE1, 0x19, 0x1A, 0xE1,
+ 0x1A, 0x01, 0x19, 0xA1, 0x19, 0xA1, 0xA9, 0xB1,
+ 0xB9, 0xE9, 0x53, 0x4B, 0x23, 0xAB, 0x53, 0x23,
+ 0xAB, 0x23, 0x2B, 0xAB, 0xAB, 0x2B, 0x23, 0xAB,
+ 0x53, 0x23, 0x2B, 0xAB, 0x53, 0xAB, 0x20,
+ /* message center timestamp */
+ 0x03, 0x06,
+ 0x13, 0x10, 0x23, 0x20, 0x06, 0x37,
+ /* priority indicator */
+ 0x08, 0x01,
+ 0x00
+ };
+
+ common_test_part_from_pdu (
+ pdu, sizeof (pdu),
+ MM_SMS_CDMA_TELESERVICE_ID_WMT,
+ MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN,
+ "3305773196",
+ 63,
+ /* this is ASCII-7 but message uses latin encoding */
+ "#$\\##\\#@#4#4567=*idujdudeuuedujdeujud");
+}
+
+static void
+test_latin_encoding_2 (void)
+{
+ static const guint8 pdu[] = {
+ /* message type */
+ 0x00,
+ /* teleservice id */
+ 0x00, 0x02,
+ 0x10, 0x02,
+ /* originating address */
+ 0x02, 0x07,
+ 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
+ /* bearer reply option */
+ 0x06, 0x01,
+ 0xFC,
+ /* bearer data */
+ 0x08, 0x1C,
+ /* message id */
+ 0x00, 0x03,
+ 0x13, 0x8D, 0x20,
+ /* user data */
+ 0x01, 0x0A,
+ 0x40, 0x42, 0x1B, 0x0B, 0x6B, 0x83, 0x2F, 0x9B,
+ 0x71, 0x08,
+ /* message center timestamp */
+ 0x03, 0x06,
+ 0x13, 0x10, 0x23, 0x20, 0x06, 0x37,
+ /* priority indicator */
+ 0x08, 0x01,
+ 0x00
+ };
+
+ common_test_part_from_pdu (
+ pdu, sizeof (pdu),
+ MM_SMS_CDMA_TELESERVICE_ID_WMT,
+ MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN,
+ "3305773196",
+ 63,
+ /* this is latin and message uses latin encoding */
+ "Campeón!");
+}
+
+static void
+test_unicode_encoding (void)
+{
+ static const guint8 pdu[] = {
+ /* message type */
+ 0x00,
+ /* teleservice id */
+ 0x00, 0x02,
+ 0x10, 0x02,
+ /* originating address */
+ 0x02, 0x07,
+ 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
+ /* bearer reply option */
+ 0x06, 0x01,
+ 0xFC,
+ /* bearer data */
+ 0x08, 0x28,
+ /* message id */
+ 0x00, 0x03,
+ 0x1B, 0x73, 0xF0,
+ /* user data */
+ 0x01, 0x16,
+ 0x20, 0x52, 0x71, 0x6A, 0xB8, 0x5A, 0xA7, 0x92,
+ 0xDB, 0xC3, 0x37, 0xC4, 0xB7, 0xDA, 0xDA, 0x82,
+ 0x98, 0xB4, 0x50, 0x42, 0x94, 0x18,
+ /* message center timestamp */
+ 0x03, 0x06,
+ 0x13, 0x10, 0x24, 0x10, 0x45, 0x28,
+ /* priority indicator */
+ 0x08, 0x01,
+ 0x00
+ };
+
+ common_test_part_from_pdu (
+ pdu, sizeof (pdu),
+ MM_SMS_CDMA_TELESERVICE_ID_WMT,
+ MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN,
+ "3305773196",
+ 63,
+ "中國哲學書電子化計劃");
+}
+
+/********************* PDU CREATOR TESTS *********************/
+
+static void
+common_test_create_pdu (MMSmsCdmaTeleserviceId teleservice_id,
+ const gchar *number,
+ const gchar *text,
+ const guint8 *data,
+ gsize data_size,
+ const guint8 *expected,
+ gsize expected_size)
+{
+ MMSmsPart *part;
+ guint8 *pdu;
+ guint len = 0;
+ GError *error = NULL;
+
+ g_assert (number != NULL);
+
+ part = mm_sms_part_new (0, MM_SMS_PDU_TYPE_CDMA_SUBMIT);
+ mm_sms_part_set_cdma_teleservice_id (part, teleservice_id);
+ mm_sms_part_set_number (part, number);
+ if (text)
+ mm_sms_part_set_text (part, text);
+ else {
+ GByteArray *data_bytearray;
+
+ data_bytearray = g_byte_array_sized_new (data_size);
+ g_byte_array_append (data_bytearray, data, data_size);
+ mm_sms_part_take_data (part, data_bytearray);
+ }
+
+ pdu = mm_sms_part_cdma_get_submit_pdu (part, &len, &error);
+
+ trace_pdu (pdu, len);
+
+ g_assert_no_error (error);
+ g_assert (pdu != NULL);
+ g_assert_cmpuint (len, ==, expected_size);
+ g_assert_cmpint (memcmp (pdu, expected, len), ==, 0);
+
+ g_free (pdu);
+}
+
+static void
+test_create_pdu_text_ascii_encoding (void)
+{
+ static const char *number = "3305773196";
+ static const char *text = "AAAA";
+ static const guint8 expected[] = {
+ /* message type */
+ 0x00,
+ /* teleservice id */
+ 0x00, 0x02,
+ 0x10, 0x02,
+ /* destination address */
+ 0x04, 0x07,
+ 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
+ /* bearer data */
+ 0x08, 0x0D,
+ 0x00, 0x03, 0x20, 0x00, 0x00, /* message id */
+ 0x01, 0x06, 0x10, 0x24, 0x18, 0x30, 0x60, 0x80 /* user_data */
+ };
+
+ common_test_create_pdu (MM_SMS_CDMA_TELESERVICE_ID_WMT,
+ number,
+ text,
+ NULL, 0,
+ expected, sizeof (expected));
+}
+
+static void
+test_create_pdu_text_latin_encoding (void)
+{
+ static const char *number = "3305773196";
+ static const char *text = "Campeón!";
+ static const guint8 expected[] = {
+ /* message type */
+ 0x00,
+ /* teleservice id */
+ 0x00, 0x02,
+ 0x10, 0x02,
+ /* destination address */
+ 0x04, 0x07,
+ 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
+ /* bearer data */
+ 0x08, 0x11,
+ /* message id */
+ 0x00, 0x03,
+ 0x20, 0x00, 0x00,
+ /* user data */
+ 0x01, 0x0A,
+ 0x40, 0x42, 0x1B, 0x0B, 0x6B, 0x83, 0x2F, 0x9B,
+ 0x71, 0x08
+ };
+
+ common_test_create_pdu (MM_SMS_CDMA_TELESERVICE_ID_WMT,
+ number,
+ text,
+ NULL, 0,
+ expected, sizeof (expected));
+}
+
+static void
+test_create_pdu_text_unicode_encoding (void)
+{
+ static const char *number = "3305773196";
+ static const char *text = "中國哲學書電子化計劃";
+ static const guint8 expected[] = {
+ /* message type */
+ 0x00,
+ /* teleservice id */
+ 0x00, 0x02,
+ 0x10, 0x02,
+ /* destination address */
+ 0x04, 0x07,
+ 0x02, 0x8C, 0xE9, 0x5D, 0xCC, 0x65, 0x80,
+ /* bearer data */
+ 0x08, 0x1D,
+ /* message id */
+ 0x00, 0x03,
+ 0x20, 0x00, 0x00,
+ /* user data */
+ 0x01, 0x16,
+ 0x20, 0x52, 0x71, 0x6A, 0xB8, 0x5A, 0xA7, 0x92,
+ 0xDB, 0xC3, 0x37, 0xC4, 0xB7, 0xDA, 0xDA, 0x82,
+ 0x98, 0xB4, 0x50, 0x42, 0x94, 0x18
+ };
+
+ common_test_create_pdu (MM_SMS_CDMA_TELESERVICE_ID_WMT,
+ number,
+ text,
+ NULL, 0,
+ expected, sizeof (expected));
+}
+
+/************************************************************/
+
+void
+_mm_log (const char *loc,
+ const char *func,
+ guint32 level,
+ const char *fmt,
+ ...)
+{
+#if defined ENABLE_TEST_MESSAGE_TRACES
+ /* Dummy log function */
+ va_list args;
+ gchar *msg;
+
+ va_start (args, fmt);
+ msg = g_strdup_vprintf (fmt, args);
+ va_end (args);
+ g_print ("%s\n", msg);
+ g_free (msg);
+#endif
+}
+
+int main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/pdu1", test_pdu1);
+ g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/invalid-parameter-length", test_invalid_parameter_length);
+ g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/invalid-address-length", test_invalid_address_length);
+ g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/created-by-us", test_created_by_us);
+ g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/latin-encoding", test_latin_encoding);
+ g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/latin-encoding-2", test_latin_encoding_2);
+ g_test_add_func ("/MM/SMS/CDMA/PDU-Parser/unicode-encoding", test_unicode_encoding);
+
+ g_test_add_func ("/MM/SMS/CDMA/PDU-Creator/ascii-encoding", test_create_pdu_text_ascii_encoding);
+ g_test_add_func ("/MM/SMS/CDMA/PDU-Creator/latin-encoding", test_create_pdu_text_latin_encoding);
+ g_test_add_func ("/MM/SMS/CDMA/PDU-Creator/unicode-encoding", test_create_pdu_text_unicode_encoding);
+
+ return g_test_run ();
+}