diff options
author | Guido Günther <agx@sigxcpu.org> | 2014-02-05 08:40:16 +0100 |
---|---|---|
committer | Guido Günther <agx@sigxcpu.org> | 2014-02-05 08:40:16 +0100 |
commit | fa2b467e288cb137ffd792becbf0c1e757d85be4 (patch) | |
tree | ce308eb0886e93805e7d88bccce48c93797fd6dd /src | |
parent | afc4b839a31c530d73b91aa2483795f185eb7e52 (diff) |
New upstream version 1.2.0upstream/1.2.0upstream
Diffstat (limited to 'src')
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, ¤t_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, + <e, + &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, ¶meter->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, ¶meter->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 (¶meter->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 (¶meter->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 (¶meter->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 (¶meter->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 (¶meter->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 (¶meter->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 (¶meter->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 (¶meter->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 (¶meter->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 (¶meter->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 (¶meter->parameter_value[0], 0, 6); + mm_dbg (" sequence: %u", sequence); + + error_class = read_bits (¶meter->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 *)¶meter->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 (); +} |